| 1 | import assert from "node:assert/strict"; |
| 2 | import { test } from "node:test"; |
| 3 | |
| 4 | import { |
| 5 | type OverviewFacts, |
| 6 | POLICY_PATHS, |
| 7 | overviewRows, |
| 8 | rowsIn, |
| 9 | securityPolicyBranch, |
| 10 | securityPolicyPullBody, |
| 11 | securityPolicyTemplate, |
| 12 | } from "./security-overview.ts"; |
| 13 | |
| 14 | const facts = (over: Partial<OverviewFacts> = {}): OverviewFacts => ({ |
| 15 | base: "/acme/rocket", |
| 16 | policyPath: null, |
| 17 | reporting: false, |
| 18 | advisories: 0, |
| 19 | upkeep: true, |
| 20 | codeScanning: { configured: false, analyses: 0, entitled: true }, |
| 21 | historyAlerts: true, |
| 22 | canManage: true, |
| 23 | canPush: true, |
| 24 | ...over, |
| 25 | }); |
| 26 | |
| 27 | test("every feature is one row with a status word and one thing to do", () => { |
| 28 | const rows = overviewRows(facts()); |
| 29 | assert.deepEqual( |
| 30 | rows.map((row) => [row.key, row.status]), |
| 31 | [ |
| 32 | ["policy", "needs_setup"], |
| 33 | ["advisories", "enabled"], |
| 34 | ["reporting", "disabled"], |
| 35 | ["dependency_alerts", "enabled"], |
| 36 | ["security_updates", "enabled"], |
| 37 | ["code_scanning", "needs_setup"], |
| 38 | ["secret_scanning", "enabled"], |
| 39 | ["code_quality", "coming"], |
| 40 | ], |
| 41 | ); |
| 42 | // No policy: a pull request adds one, for someone who can push. |
| 43 | assert.deepEqual(rows[0].action, { kind: "post", label: "Set up a security policy", action: "/acme/rocket/security/policy/setup", pending: "Opening a pull request…" }); |
| 44 | // Code scanning not set up: the starter workflow, for an admin on an entitled repository. |
| 45 | assert.equal(rows[5].action?.kind, "post"); |
| 46 | // Switches are for admins; the words say what they switch. |
| 47 | assert.deepEqual(rows[2].action, { kind: "switch", label: "Private vulnerability reporting", intent: "reporting", on: false }); |
| 48 | assert.deepEqual(rows[4].action, { kind: "switch", label: "Security updates", intent: "upkeep", on: true }); |
| 49 | // Code quality is honest about not being there. |
| 50 | assert.equal(rows[7].action, null); |
| 51 | assert.equal(rowsIn(rows, "findings").length, 5); |
| 52 | assert.equal(rowsIn(rows, "reporting").length, 2); |
| 53 | }); |
| 54 | |
| 55 | test("what is on reads as enabled, with a link to it", () => { |
| 56 | const rows = overviewRows(facts({ policyPath: "SECURITY.md", reporting: true, advisories: 2, codeScanning: { configured: true, analyses: 3, entitled: true } })); |
| 57 | assert.equal(rows[0].status, "enabled"); |
| 58 | assert.deepEqual(rows[0].action, { kind: "link", label: "View policy", to: "/acme/rocket/blob/HEAD/SECURITY.md" }); |
| 59 | assert.equal(rows[1].about, "2 published advisories tell people who depend on this repository what to do."); |
| 60 | assert.equal(rows[2].status, "enabled"); |
| 61 | assert.equal(rows[5].status, "enabled"); |
| 62 | assert.deepEqual(rows[5].action, { kind: "link", label: "View alerts", to: "/acme/rocket/security/code-scanning" }); |
| 63 | }); |
| 64 | |
| 65 | test("someone who cannot change settings sees the rows without the switches", () => { |
| 66 | const rows = overviewRows(facts({ canManage: false, canPush: false, codeScanning: { configured: false, analyses: 0, entitled: false } })); |
| 67 | assert.equal(rows[0].action, null); |
| 68 | assert.equal(rows[2].action, null); |
| 69 | assert.equal(rows[4].action, null); |
| 70 | // Code scanning on a private repository without the plan: a page that says so, not a form. |
| 71 | assert.deepEqual(rows[5].action, { kind: "link", label: "Learn more", to: "/acme/rocket/security/code-scanning" }); |
| 72 | assert.match(rows[5].hint ?? "", /g1t plan/); |
| 73 | assert.equal(rows[6].hint, "Push protection is always on."); |
| 74 | assert.match(overviewRows(facts({ historyAlerts: false }))[6].about, /history are off/); |
| 75 | }); |
| 76 | |
| 77 | test("the policy template says how to report and names the private form when it is on", () => { |
| 78 | const withForm = securityPolicyTemplate("acme/rocket", true, "/acme/rocket"); |
| 79 | assert.match(withForm, /^# Security policy/); |
| 80 | assert.match(withForm, /https:\/\/g1t\.sh\/acme\/rocket\/security\/report/); |
| 81 | assert.match(withForm, /## Supported versions/); |
| 82 | const without = securityPolicyTemplate("acme/rocket", false, "/acme/rocket"); |
| 83 | assert.doesNotMatch(without, /security\/report/); |
| 84 | assert.match(without, /Email the maintainers/); |
| 85 | assert.match(securityPolicyPullBody("SECURITY.md"), /`SECURITY\.md`/); |
| 86 | assert.equal(securityPolicyBranch([]), "add-security-policy"); |
| 87 | assert.equal(securityPolicyBranch(["add-security-policy", "add-security-policy-2"]), "add-security-policy-3"); |
| 88 | assert.equal(POLICY_PATHS[0], "SECURITY.md"); |
| 89 | }); |