366 lines · 12.1 KB
import { env } from "cloudflare:workers";
import { chunks } from "../db";
import { repoDb, stmt, type ScopedStatement } from "../scoped-db";
import { fromBase64, randomId, sha256Hex } from "../crypto";
import { badRequest } from "../errors";
import { parseSarif } from "./sarif";
import type { SarifResult } from "./sarif";
import { notifyCodeScanningAlerts } from "../notify/security";
import { reserveSecurityNumbers } from "./settings";

const MAX_SARIF_BYTES = 10 * 1024 * 1024;
const MAX_UPLOADS_PER_DAY = 200;
const DAY_MS = 24 * 60 * 60 * 1000;
const STATEMENTS_PER_BATCH = 50;
const MAX_TOOLS_JSON = 4096;
const ALERT_PAGE = 50;
const GZIP_MAGIC = [0x1f, 0x8b];

export const SARIF_LIMITS = { maxBytes: MAX_SARIF_BYTES, uploadsPerDay: MAX_UPLOADS_PER_DAY };

export function sarifKey(repoId: string, uploadId: string): string {
  return `sarif/${repoId}/${uploadId}.json`;
}

/** Accepts GitHub's encoding (base64 of gzipped SARIF) as well as base64 of plain JSON. */
export async function decodeSarifPayload(encoded: string): Promise<Uint8Array<ArrayBuffer>> {
  let bytes: Uint8Array<ArrayBuffer>;
  try {
    bytes = fromBase64(encoded.replace(/\s+/g, ""));
  } catch {
    throw badRequest("sarif must be base64 encoded");
  }
  if (bytes[0] !== GZIP_MAGIC[0] || bytes[1] !== GZIP_MAGIC[1]) return bytes;
  const stream = new Blob([bytes]).stream().pipeThrough(new DecompressionStream("gzip"));
  const reader = stream.getReader();
  const parts: Uint8Array[] = [];
  let total = 0;
  while (true) {
    const { value, done } = await reader.read();
    if (done) break;