Skip to content

g1t/apps/web/app/lib/security-suite.test.ts

145 lines6,043 bytesCodeBlame

Pick any line to see why it is the way it is: the commit, the pull request and issue it came from, and what the agent was thinking.

Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar1import assert from "node:assert/strict";
2import { test } from "node:test";
3
4import type { CodeAlert, SecretFinding } from "@g1t/contracts";
5
6import {
7 codeFilters,
8 codeScanningBranch,
9 codeScanningPullBody,
10 codeScanningWorkflow,
11 countStates,
12 keepCode,
13 keepSecret,
14 legacySecurityTarget,
15 secretFilters,
16 secretTypes,
17 severityCounts,
18 total,
19 trendMax,
20} from "./security-suite.ts";
21
22const secret = (over: Partial<SecretFinding> = {}): SecretFinding => ({
23 id: "sec_1",
24 repoId: "r",
25 kind: "github_token",
26 label: "a GitHub token",
27 path: "scripts/release.sh",
28 line: 12,
29 commit: "abcdef1234",
30 preview: "ghp_X7…",
31 status: "open",
32 source: "push",
33 foundBy: "ana",
34 foundAt: "2026-10-06T09:00:00.000Z",
35 decidedBy: null,
36 reason: null,
37 decidedAt: null,
38 state: "open",
39 ...over,
40});
41
42const code = (over: Partial<CodeAlert> = {}): CodeAlert => ({
43 id: "cod_1",
44 number: 1,
45 repoId: "r",
46 tool: "Semgrep OSS",
47 category: "Semgrep OSS",
48 ruleId: "rule",
49 ruleName: null,
50 ruleDescription: null,
51 help: null,
52 helpUri: null,
53 tags: [],
54 level: "error",
55 securitySeverity: null,
56 severity: "high",
57 message: "m",
58 path: "a.js",
59 startLine: 1,
60 endLine: 1,
61 startColumn: null,
62 endColumn: null,
63 state: "open",
64 fingerprint: "f",
65 firstCommit: "c",
66 lastCommit: "c",
67 createdAt: "",
68 updatedAt: "",
69 fixedAt: null,
70 dismissedBy: null,
71 dismissedReason: null,
72 dismissedComment: null,
73 dismissedAt: null,
74 issue: null,
75 ...over,
76});
77
78test("secret filters are read from the address and narrow the list", () => {
79 const filters = secretFilters(new URLSearchParams("state=open&validity=active&bypassed=true"));
80 assert.deepEqual(filters, { state: "open", type: null, validity: "active", bypassed: true });
81 const bypassed = secret({ validity: "active", bypass: { reason: "will_fix_later", comment: null, by: "ana", at: "x", approvedBy: null } });
82 assert.equal(keepSecret(bypassed, filters), true);
83 assert.equal(keepSecret(secret({ validity: "active" }), filters), false);
84 // Unknown words fall back to the defaults.
85 assert.deepEqual(secretFilters(new URLSearchParams("state=everything&validity=maybe")), { state: "open", type: null, validity: null, bypassed: null });
86 // Never asked counts as unknown.
87 assert.equal(keepSecret(secret(), { state: "open", type: null, validity: "unknown", bypassed: null }), true);
88});
89
90test("secret types are listed once each, custom patterns by name", () => {
91 const types = secretTypes([secret(), secret({ id: "sec_2" }), secret({ kind: "custom_pattern", patternName: "Acme key", label: "a match for the custom pattern \"Acme key\"" })]);
92 assert.deepEqual(types, [["github_token", "GitHub token"], ["custom_pattern", "Custom: Acme key"]].sort((a, b) => a[1].localeCompare(b[1])));
93});
94
95test("code filters and counts", () => {
96 const filters = codeFilters(new URLSearchParams("severity=high&tool=Semgrep OSS"));
97 assert.equal(keepCode(code(), filters), true);
98 assert.equal(keepCode(code({ severity: "low" }), filters), false);
99 assert.equal(keepCode(code({ state: "fixed" }), filters), false);
100 assert.deepEqual(countStates([code(), code({ state: "fixed" }), code({ state: "fixed" })]), { open: 1, dismissed: 0, fixed: 2 });
101 const counts = severityCounts([code(), code({ severity: "critical" }), code({ state: "dismissed" })]);
102 assert.deepEqual(counts, { critical: 1, high: 1, medium: 0, low: 0, unknown: 0 });
103 assert.equal(total(counts), 2);
104});
105
106test("old Security links go to the sections that replaced their tabs", () => {
107 const base = "/acme/rocket";
108 assert.equal(legacySecurityTarget(base, new URLSearchParams("tab=secrets&finding=sec_9")), "/acme/rocket/security/secret-scanning/sec_9");
109 assert.equal(legacySecurityTarget(base, new URLSearchParams("finding=vul_3")), "/acme/rocket/security/vulnerabilities?finding=vul_3");
110 assert.equal(legacySecurityTarget(base, new URLSearchParams("tab=dependencies&state=fixed")), "/acme/rocket/security/vulnerabilities?state=fixed");
111 assert.equal(legacySecurityTarget(base, new URLSearchParams("")), null);
112});
113
114test("the starter workflow scans each language it finds, then uploads SARIF for the right ref", () => {
115 const yaml = codeScanningWorkflow("main");
116 assert.match(yaml, /branches: \["main"\]/);
117 // A scanner per language, each run only when the repository has it.
118 assert.match(yaml, /if: steps\.languages\.outputs\.python == 'true'/);
119 assert.match(yaml, /bandit --recursive \. .*\n.*--format sarif --output \/tmp\/sarif\/python\.sarif/);
120 assert.match(yaml, /if: steps\.languages\.outputs\.go == 'true'/);
121 assert.match(yaml, /-fmt sarif -out "\$out" \.\/\.\.\./);
122 assert.match(yaml, /if: steps\.languages\.outputs\.javascript == 'true'/);
123 assert.match(yaml, /eslint-plugin-security/);
124 assert.match(yaml, /@microsoft\/eslint-formatter-sarif/);
125 assert.match(yaml, /if: steps\.languages\.outputs\.rust == 'true'/);
126 assert.match(yaml, /cargo clippy --all-targets --message-format=json/);
127 assert.match(yaml, /clippy-sarif/);
128 assert.ok(!/semgrep/i.test(yaml), "no Semgrep");
129 // Each language's results under their own category.
130 for (const category of ["python", "javascript", '"go"', '"rust"']) assert.ok(yaml.includes(category), category);
131 assert.match(yaml, /--arg category "\$category"/);
132 assert.match(yaml, /refs\/pull\/\$\(jq -r \.number "\$GITHUB_EVENT_PATH"\)\/head/);
133 assert.match(yaml, /--rawfile sarif "\$file\.b64"/);
134 assert.match(yaml, /\/code-scanning\/sarifs/);
135 assert.match(yaml, /\$\{\{ secrets\.G1T_TOKEN \}\}/);
136 assert.ok(!yaml.includes("\t"), "YAML takes no tabs");
137 assert.match(codeScanningPullBody("main"), /Code scanning\*\* check/);
138 assert.equal(codeScanningBranch([]), "add-code-scanning");
139 assert.equal(codeScanningBranch(["add-code-scanning", "add-code-scanning-2"]), "add-code-scanning-3");
140});
141
142test("a trend is scaled to its tallest day", () => {
143 assert.equal(trendMax([]), 1);
144 assert.equal(trendMax([{ day: "d", secretScanning: 1, codeScanning: 2, vulnerability: 3 }]), 6);
145});