[Reportbug-maint] Bug#590214: (half) patch to receive reportbug submission via HTTP transport

Stefano Zacchiroli zack at debian.org
Wed Jun 8 09:14:46 UTC 2011


tags 590269 + patch
thanks

As briefly discussed on IRC, here is (half) patch to implement this
feature request. It's a CGI that:

- receive via HTTP file upload a bug report. The bug report is meant to
  prepared on the user machine by reportbug as usual, so by default it
  will contain all the information that mail-submitted bug reports
  contain

- do some syntax checking on the bug report, and fail with an HTTP error
  code if that fails

- do some mail header sanitization to reduce SPAM effects. AFAICT, after
  sanitization the SPAM risks of using the CGI would be the same that we
  have at present with the mail-based submission interface

It's only half a patch because, for complete testing, we also need a
patch for #590214 (Cc:-ed), i.e. we need support in reportbug for
delivering a MIME bug report via HTTP upload rather than via mail. If a
kind soul can write this, I'll be happy to set up a test instance of the
script for more wide testing.

Note that at present, the following testing path does *not* work
properly:

- prepare a bug report with reportbug
- save it as a draft on disk and quit
- upload the file using the CGI

because the "save to draft" feature of reportbug does not save the
entire MIME bug report (e.g. it lacks attachments and other details). So
we really need proper HTTP upload support in reportbug to proceed.

If nevertheless someone want to test the attached CGI, you need to set
$bts_to to 'submit at bugs.debian.org' and $DEBUG to 0 (see comments in the
source code).

Feedback is welcome,
Cheers.
-- 
Stefano Zacchiroli -o- PhD in Computer Science \ PostDoc @ Univ. Paris 7
zack@{upsilon.cc,pps.jussieu.fr,debian.org} -<>- http://upsilon.cc/zack/
Quando anche i santi ti voltano le spalle, |  .  |. I've fans everywhere
ti resta John Fante -- V. Capossela .......| ..: |.......... -- C. Adams
-------------- next part --------------
#!/usr/bin/perl -w
#
# http-submit.cgi - HTTP bug submission gateway for Debbugs
# Copyright (C) 2011  Stefano Zacchiroli <zack at debian.org>
#
# This program free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License version 2 as published by the
# Free Software Foundation; either Version 2 of the License, or (at your
# option) any later version.

use strict;

use CGI;
use MIME::Parser;


# Max bug report size.  Note: this check is only to avoid in-memory parsing of
# (too) large MIME entities. Proper defense against ridiculously large bug
# reports requires quota at the HTTP level (e.g. Apache's LimitRequestBody).
my $max_report_size = 10 * 2 ** 20;	#  (= 10 Mb)
my $max_headers = 50;

my $bts_to = '';	# set this to 'submit at bugs.debian.org' for production use
my $DEBUG = 1;		# debug / dry run mode; must be set to 0 for production use

my $howto_url = "http://www.debian.org/Bugs/Reporting";
my $sendmail = "/usr/sbin/sendmail";
my %http_status = (
    ok       => "200 OK",
    badreq   => "400 Bad Request",
    toobig   => "413 Request Entity Too Large",
    internal => "500 Internal Server Error",
    );


# debug routine
sub dump_mime($$) {
    my ($q, $mime) = @_;

    print $q->header(-type=>"text/html", -status=>$http_status{'ok'}),
	$q->start_html,
	$q->h1('debug / dry run'), "\n",
	$q->pre($q->escapeHTML($mime->stringify)), "\n",
	$q->end_html;
}

# abort CGI execution returning an HTTP error
sub error($$$) {
    my ($q, $status, $msg) = @_;

    print $q->header(-type => "text/html", -status => $status),
        $q->start_html($status),
        $q->h1($status), "\n",
        $q->p($msg), "\n",
        $q->p("Abort."),
        $q->end_html;
    exit(0);
}

# extract a bug report submitted via HTTP file upload (RFC 1867)
sub get_report($) {
    my ($q) = @_;

    my $fh = $q->upload('report');
    if (! defined($fh)) {	# no file uploaded (i.e. no bug report)
	error($q, $http_status{'badreq'}, "No bug report has been submitted.");
    }
    if (! $fh && $q->cgi_error) {	# cgi error (e.g. upload aborted)
	error($q, $q->cgi_error, "CGI submission error.");
    }
    my $handle = $fh->handle;	# get an IO::Handle
    if (($handle->stat)[7] > $max_report_size) {
	error($q, $http_status{'toobig'}, "Bug report is too large.");
    }

    return $handle;
}

# check that the bug report body conforms to debbugs submission syntax
sub check_syntax($) {
    my ($body) = @_;

    my $io = $body->open("r");
    my $line = <$io> or return 0;
    return 0 unless $line =~ /^Package:\s+[a-z0-9+.-]/;	# starts w/ Package:

    my $in_headers = 1;
    my $headers_no = 1;
    while (defined(my $line = <$io>)) {
	if ($line =~ /^\w+:\s+/) {	# starts w/ a (pseudo) header block...
	    $headers_no++;
	    return 0 if ($headers_no > $max_headers);
	} elsif ($in_headers && $line =~ /^\s+$/) { # ...followed by empty line
	    $in_headers = 0;
	    last;
	}
    }
    $io->close;

    return 1;
}

# extract, validate, and sanitize a MIME::Entity bug report
sub get_mime($$) {
    my ($q, $fh) = @_;

    my $parser = new MIME::Parser;
    $parser->output_to_core(1);
    my $mime = $parser->parse($fh)
	or error($q, $http_status{'badreq'}, "Cannot parse bug report.");

    my $head = $mime->head;	# sanitize header to avoid relay/spam issues
    if ($head->count("From") != 1) {
	error($q, $http_status{'badreq'}, "Malformed From header.");
    }
    $head->replace("To", $bts_to);
    $head->delete("Cc");
    $head->delete("Bcc");

    my $mime_type = $mime->mime_type;
    my $body = undef;
    if ($mime_type eq "text/plain") {
	$body = $mime->bodyhandle;
    } elsif ($mime_type eq "multipart/mixed") {
	$body = $mime->parts(0)->bodyhandle;
    } else {
	error($q, $http_status{'badreq'}, "Unsupported MIME type: $mime_type.");
    }
    if (! check_syntax($body)) {
	error($q, $http_status{'badreq'},
	      "Syntax error, check <a href=\"$howto_url\">instructions</a>.");
    }

    return $mime;
}

# deliver bug report to (local) mail transport agent
sub deliver($$) {
    my ($q, $mime) = @_;

    if ($DEBUG) { dump_mime($q, $mime); exit(0); }

    open MAIL, "| $sendmail -t -oi"
	or error($q, $http_status{internal}, "Cannot deliver report.");
    $mime->print(\*MAIL);
    close MAIL;
}


sub main() {
    my $q = CGI->new;
    my $report = get_report($q);
    my $mime = get_mime($q, $report);
    deliver($q, $mime);

    print $q->header(-type=>"text/html", -status=>$http_status{'ok'}),
        $q->start_html,
        $q->p("Bug report received."), "\n",
        $q->p("Thanks."),
        $q->end_html;
}

main();
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 190 bytes
Desc: Digital signature
URL: <http://lists.alioth.debian.org/pipermail/reportbug-maint/attachments/20110608/df9ecbe7/attachment.pgp>


More information about the Reportbug-maint mailing list