create-safest-tools 0.4.1 → 0.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2595,7 +2595,10 @@ async function handleAdminAiQualitySample(
2595
2595
  const sample = await loadAiQualitySample(env.DB, sampleId);
2596
2596
  if (!sample) throw new ApiError(404, "quality_sample_not_found", "The quality sample does not exist.");
2597
2597
  await assertOperatorReportAccess(env.DB, session, String(sample.reportId));
2598
- const report = await loadReportDetail(env.DB, String(sample.reportId));
2598
+ const report = await loadReportDetail(env.DB, String(sample.reportId), {
2599
+ contactEncryptionKey: env.CREDENTIAL_ENCRYPTION_KEY,
2600
+ includeReporterEmail: session.permissions.messageReporters,
2601
+ });
2599
2602
  if (!report) throw new ApiError(404, "report_not_found", "The sampled report does not exist.");
2600
2603
  return json({ schema_version: "1", sample: projectOperatorFields(session, "quality_sample", sample), report: projectOperatorFields(session, "report", report) });
2601
2604
  }
@@ -2843,7 +2846,10 @@ async function handleAdminReportDetail(request: Request, env: Env, reportId: str
2843
2846
  if (request.method !== "GET") return methodNotAllowed(["GET"]);
2844
2847
  const session = await requireOperator(request, env, "readReports");
2845
2848
  await assertOperatorReportAccess(env.DB, session, reportId);
2846
- const report = await loadReportDetail(env.DB, reportId);
2849
+ const report = await loadReportDetail(env.DB, reportId, {
2850
+ contactEncryptionKey: env.CREDENTIAL_ENCRYPTION_KEY,
2851
+ includeReporterEmail: session.permissions.messageReporters,
2852
+ });
2847
2853
  if (!report) throw new ApiError(404, "report_not_found", "The report does not exist.");
2848
2854
  return json({ schema_version: "1", report: projectOperatorFields(session, "report", report) });
2849
2855
  }
@@ -1,4 +1,5 @@
1
1
  import { AUDIT_GENESIS_HASH, canonicalJson, hashAuditEntry } from "./audit";
2
+ import { decryptReportContact } from "./report-crypto";
2
3
  import { selectParticipantDelivery, type ParticipantDelivery } from "./report-email";
3
4
  import { ApiError } from "./report-http";
4
5
  import { analystAvatarColor } from "./report-presence";
@@ -160,7 +161,11 @@ export async function releaseReportClaim(db: D1Database, reportId: string, sessi
160
161
  ]);
161
162
  }
162
163
 
163
- export async function loadReportDetail(db: D1Database, reportId: string): Promise<Record<string, unknown> | null> {
164
+ export async function loadReportDetail(
165
+ db: D1Database,
166
+ reportId: string,
167
+ options: { contactEncryptionKey?: string; includeReporterEmail?: boolean } = {},
168
+ ): Promise<Record<string, unknown> | null> {
164
169
  const report = await db.prepare(`
165
170
  SELECT r.id, r.public_reference AS reference, r.source, r.state, r.priority, r.reason_code AS reasonCode,
166
171
  r.locale, r.queue_id AS queueId, r.submitted_at AS submittedAt, r.received_at AS receivedAt,
@@ -172,7 +177,9 @@ export async function loadReportDetail(db: D1Database, reportId: string): Promis
172
177
  qv.management_mode AS queueManagementMode, qv.reviewer_instructions AS reviewerInstructions,
173
178
  t.target_type AS targetType, t.target_reference AS targetReference,
174
179
  t.owner_reference AS ownerReference, t.customer_url AS customerUrl,
175
- p.reference AS reporterReference
180
+ p.reference AS reporterReference, p.contact_mode AS reporterContactMode,
181
+ p.contact_ciphertext AS reporterContactCiphertext,
182
+ p.reference_redacted_at AS reporterReferenceRedactedAt
176
183
  FROM reports r
177
184
  JOIN report_targets t ON t.report_id = r.id
178
185
  JOIN report_participants p ON p.report_id = r.id AND p.audience = 'reporter'
@@ -266,8 +273,35 @@ export async function loadReportDetail(db: D1Database, reportId: string): Promis
266
273
  value: parsedJson(row.valueJson, null),
267
274
  });
268
275
  }
276
+ let reporterEmail: string | null = null;
277
+ let reporterContactStatus: "available" | "not_provided" | "redacted" | "restricted" | "unavailable";
278
+ if (report.reporterReferenceRedactedAt) {
279
+ reporterContactStatus = "redacted";
280
+ } else if (!report.reporterContactCiphertext) {
281
+ reporterContactStatus = "not_provided";
282
+ } else if (!options.includeReporterEmail) {
283
+ reporterContactStatus = "restricted";
284
+ } else if (!options.contactEncryptionKey) {
285
+ reporterContactStatus = "unavailable";
286
+ } else {
287
+ try {
288
+ reporterEmail = (await decryptReportContact(
289
+ options.contactEncryptionKey,
290
+ String(report.reporterContactCiphertext),
291
+ reportId,
292
+ "reporter",
293
+ )).trim().toLowerCase();
294
+ reporterContactStatus = reporterEmail ? "available" : "unavailable";
295
+ } catch {
296
+ reporterContactStatus = "unavailable";
297
+ }
298
+ }
269
299
  return {
270
300
  ...report,
301
+ reporterEmail,
302
+ reporterContactStatus,
303
+ reporterContactCiphertext: undefined,
304
+ reporterReferenceRedactedAt: undefined,
271
305
  policyRules: (() => {
272
306
  try {
273
307
  const value: unknown = JSON.parse(String(report.policyRulesJson ?? "[]"));
@@ -1,19 +1,19 @@
1
1
  /* eslint-disable */
2
- // Generated by Wrangler by running `wrangler types --env-file=config/dev.vars.example --include-runtime=false --strict-vars=false` (hash: 2cca359aa7fa3dcf738a197c47478066)
2
+ // Generated by Wrangler by running `wrangler types --env-file=config/dev.vars.example --include-runtime=false --strict-vars=false` (hash: 2d2c98e01480f3c1492835530279c5f1)
3
3
  interface __BaseEnv_Env {
4
4
  PROFILE_MEDIA: R2Bucket;
5
5
  WORKFLOW_ARTIFACTS: R2Bucket;
6
6
  EVIDENCE_OBJECTS: R2Bucket;
7
7
  EXPORTS: R2Bucket;
8
8
  DB: D1Database;
9
+ AUTH_EMAIL: SendEmail;
10
+ REPORTER_EMAIL: SendEmail;
9
11
  CONTROL_JOBS: Queue;
10
12
  REPORT_JOBS: Queue;
11
13
  DELIVERY_JOBS: Queue;
12
14
  LOADER: WorkerLoader;
13
15
  AI: Ai;
14
16
  ASSETS: Fetcher;
15
- AUTH_EMAIL: SendEmail;
16
- REPORTER_EMAIL: SendEmail;
17
17
  PUBLIC_BASE_URL: string;
18
18
  ALLOWED_ORIGINS: string;
19
19
  CONTEXT_TOKEN_TTL_SECONDS: string;