Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Always require a subject to be reported. #7685

Merged
merged 1 commit into from
Apr 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions app/lib/admin/models.dart
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ class ModerationCase extends db.ExpandoModel<String> {
/// - `package:<package>`
/// - `package-version:<package>/<version>`
/// - `publisher:<publisherId>`
@db.StringProperty()
String? subject;
@db.StringProperty(required: true)
late String subject;

/// The `caseId` of the appeal (or null).
@db.StringProperty()
Expand Down Expand Up @@ -74,7 +74,7 @@ class ModerationCase extends db.ExpandoModel<String> {
required this.source,
required this.kind,
required this.status,
this.subject,
required this.subject,
}) {
id = caseId;
opened = clock.now().toUtc();
Expand Down Expand Up @@ -173,7 +173,6 @@ class ModerationSubject {
}

class ModerationSubjectKind {
static const unspecified = 'unspecified';
static const package = 'package';
static const packageVersion = 'package-version';
static const publisher = 'publisher';
Expand Down
27 changes: 11 additions & 16 deletions app/lib/frontend/handlers/report.dart
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,11 @@ Future<shelf.Response> reportPageHandler(shelf.Request request) async {
}

final subjectParam = request.requestedUri.queryParameters['subject'];
ModerationSubject? subject;
if (subjectParam != null) {
subject = ModerationSubject.tryParse(subjectParam);
if (subject == null) {
throw InvalidInputException('Invalid "subject" parameter.');
}
await _verifySubject(subject);
}

InvalidInputException.checkNotNull(subjectParam, 'subject');
final subject = ModerationSubject.tryParse(subjectParam!);
InvalidInputException.check(subject != null, 'Invalid "subject" parameter.');
await _verifySubject(subject!);

return htmlResponse(
renderReportPage(
Expand Down Expand Up @@ -94,12 +91,10 @@ Future<String> processReportPageHandler(
InvalidInputException.checkNull(form.email, 'email');
}

ModerationSubject? subject;
if (form.subject != null) {
subject = ModerationSubject.tryParse(form.subject!);
InvalidInputException.check(subject != null, 'Invalid subject.');
await _verifySubject(subject);
}
InvalidInputException.checkNotNull(form.subject, 'subject');
final subject = ModerationSubject.tryParse(form.subject!);
InvalidInputException.check(subject != null, 'Invalid subject.');
await _verifySubject(subject!);

InvalidInputException.checkStringLength(
form.message,
Expand All @@ -117,14 +112,14 @@ Future<String> processReportPageHandler(
source: ModerationDetectedBy.externalNotification,
kind: ModerationKind.notification,
status: ModerationStatus.pending,
subject: subject?.fqn,
subject: subject.fqn,
);
tx.insert(mc);
});

final bodyText = <String>[
'New report recieved on ${now.toIso8601String()}: $caseId',
if (subject != null) 'Subject: ${subject.fqn}',
'Subject: ${subject.fqn}',
'Message:\n${form.message}',
].join('\n\n');

Expand Down
118 changes: 115 additions & 3 deletions app/test/frontend/handlers/report_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ void main() {
testWithProfile('page does not require authentication', fn: () async {
await expectHtmlResponse(
await issueGet(
'/report',
'/report?subject=package:oxygen',
headers: {'cookie': '$experimentalCookieName=report'},
),
present: [
Expand All @@ -34,14 +34,30 @@ void main() {
final cookies = await acquireSessionCookies('user@pub.dev');
await expectHtmlResponse(
await issueGet(
'/report',
'/report?subject=package:oxygen',
headers: {'cookie': '$experimentalCookieName=report; $cookies'},
),
present: ['Please describe the issue you want to report:'],
absent: ['Contact information'],
);
});

testWithProfile('page with missing subject', fn: () async {
await expectHtmlResponse(
await issueGet(
'/report',
headers: {'cookie': '$experimentalCookieName=report'},
),
present: [
'&quot;subject&quot; cannot be `null`',
],
absent: [
'Please describe the issue you want to report:',
],
status: 400,
);
});

testWithProfile('page with bad subject', fn: () async {
await expectHtmlResponse(
await issueGet(
Expand Down Expand Up @@ -114,6 +130,100 @@ void main() {
});
});

testWithProfile('subject missing', fn: () async {
await withHttpPubApiClient(
experimental: {'report'},
fn: (client) async {
await expectApiException(
client.postReport(ReportForm(
email: 'user@pub.dev',
message: 'Huston, we have a problem.',
)),
status: 400,
code: 'InvalidInput',
message: '\"subject\" cannot be `null`',
);
expect(fakeEmailSender.sentMessages, isEmpty);
},
);
});

testWithProfile('subject is invalid', fn: () async {
await withHttpPubApiClient(
experimental: {'report'},
fn: (client) async {
await expectApiException(
client.postReport(ReportForm(
email: 'user@pub.dev',
subject: 'x',
message: 'Huston, we have a problem.',
)),
status: 400,
code: 'InvalidInput',
message: 'Invalid subject.',
);
expect(fakeEmailSender.sentMessages, isEmpty);
},
);
});

testWithProfile('package missing', fn: () async {
await withHttpPubApiClient(
experimental: {'report'},
fn: (client) async {
await expectApiException(
client.postReport(ReportForm(
email: 'user@pub.dev',
subject: 'package:x',
message: 'Huston, we have a problem.',
)),
status: 404,
code: 'NotFound',
message: 'Package \"x\" does not exist.',
);
expect(fakeEmailSender.sentMessages, isEmpty);
},
);
});

testWithProfile('version missing', fn: () async {
await withHttpPubApiClient(
experimental: {'report'},
fn: (client) async {
await expectApiException(
client.postReport(ReportForm(
email: 'user@pub.dev',
subject: 'package-version:oxygen/4.0.0',
message: 'Huston, we have a problem.',
)),
status: 404,
code: 'NotFound',
message: 'Package version \"oxygen/4.0.0\" does not exist.',
);
expect(fakeEmailSender.sentMessages, isEmpty);
},
);
});

testWithProfile('publisher missing', fn: () async {
await withHttpPubApiClient(
experimental: {'report'},
fn: (client) async {
await expectApiException(
client.postReport(ReportForm(
email: 'user@pub.dev',
subject: 'publisher:unknown-domain.com',
message: 'Huston, we have a problem.',
)),
status: 404,
code: 'NotFound',
message: 'Publisher \"unknown-domain.com\" does not exist.',
);
expect(fakeEmailSender.sentMessages, isEmpty);
},
);
});

testWithProfile('too short message', fn: () async {
await withFakeAuthRequestContext('user@pub.dev', () async {
final sessionId = requestContext.sessionData?.sessionId;
Expand All @@ -125,6 +235,7 @@ void main() {
fn: (client) async {
await expectApiException(
client.postReport(ReportForm(
subject: 'package:oxygen',
message: 'Problem.',
)),
status: 400,
Expand All @@ -143,6 +254,7 @@ void main() {
fn: (client) async {
final msg = await client.postReport(ReportForm(
email: 'user2@pub.dev',
subject: 'package:oxygen',
message: 'Huston, we have a problem.',
));

Expand All @@ -154,7 +266,7 @@ void main() {
expect(email.ccRecipients.single.email, 'user2@pub.dev');

final mc = await dbService.query<ModerationCase>().run().single;
expect(mc.subject, isNull);
expect(mc.subject, 'package:oxygen');
expect(mc.reporterEmail, 'user2@pub.dev');
},
);
Expand Down
2 changes: 1 addition & 1 deletion pkg/pub_integration/test/report_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ void main() {
await page.gotoOrigin('/experimental?report=1');
await Future.delayed(Duration(seconds: 1));

await page.gotoOrigin('/report');
await page.gotoOrigin('/report?subject=package:oxygen');
await page.waitFocusAndType('#report-email', 'user@pub.dev');
await page.waitFocusAndType(
'#report-message', 'Huston, we have a problem.');
Expand Down
Loading