From 70aa23de080bbd74546400e0a7d4265e22f0f6e6 Mon Sep 17 00:00:00 2001 From: daedalus-omt <268206840+daedalus-omt@users.noreply.github.com> Date: Wed, 15 Jul 2026 18:00:29 +0000 Subject: [PATCH 1/2] feat: project public security policy Signed-off-by: daedalus-omt <268206840+daedalus-omt@users.noreply.github.com> --- src/archetypes.ts | 14 +++++++++++++- tests/render.test.ts | 15 +++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/src/archetypes.ts b/src/archetypes.ts index 4512225..8f84082 100644 --- a/src/archetypes.ts +++ b/src/archetypes.ts @@ -2898,7 +2898,19 @@ function repoDocEnabled( key: "readme" | "contributing" | "security", fallback: boolean ): boolean { - return manifest.repo.docs?.[key] ?? fallback; + if (manifest.repo.docs?.[key] !== undefined) { + return manifest.repo.docs[key]; + } + + // Public repositories need a discoverable vulnerability-reporting route even + // when the manifest predates the v2 docs block. The generated policy uses + // GitHub's private advisory flow and does not add a contact address. An + // explicit `repo.docs.security: false` remains the migration opt-out. + if (key === "security") { + return manifest.project.visibility === "public"; + } + + return fallback; } function envExampleEnabled(manifest: BootstrapManifest): boolean { diff --git a/tests/render.test.ts b/tests/render.test.ts index 2c6e384..dccbb6f 100644 --- a/tests/render.test.ts +++ b/tests/render.test.ts @@ -190,6 +190,21 @@ describe("renderManagedFiles", () => { }); + it("projects SECURITY.md for public repositories unless explicitly disabled", () => { + const publicManifest = normalizeManifest({ + project: { name: "public-policy", owner: "acme", visibility: "public" }, + archetype: { kind: "generic-empty" } + }); + expect(renderManagedFiles(publicManifest).some((file) => file.path === "SECURITY.md")).toBe(true); + + const optedOutManifest = normalizeManifest({ + project: { name: "public-policy", owner: "acme", visibility: "public" }, + repo: { docs: { security: false } }, + archetype: { kind: "generic-empty" } + }); + expect(renderManagedFiles(optedOutManifest).some((file) => file.path === "SECURITY.md")).toBe(false); + }); + it("renders version 2 docs, templates, environment, and workflow switches", () => { const manifest = normalizeManifest({ version: 2, From c4b8603544368ea108bf3baa15643d8cdf8843ab Mon Sep 17 00:00:00 2001 From: Daedalus <268206840+daedalus-omt@users.noreply.github.com> Date: Thu, 16 Jul 2026 06:43:11 +0000 Subject: [PATCH 2/2] fix: preserve public security docs defaults Signed-off-by: Daedalus <268206840+daedalus-omt@users.noreply.github.com> --- src/manifest.ts | 2 +- src/types.ts | 2 +- tests/render.test.ts | 11 +++++++++++ 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/manifest.ts b/src/manifest.ts index a0771e3..84611e3 100644 --- a/src/manifest.ts +++ b/src/manifest.ts @@ -579,7 +579,7 @@ function normalizeRepo(repo: z.input["repo"]): BootstrapM docs: { readme: repo.docs.readme ?? true, contributing: repo.docs.contributing ?? true, - security: repo.docs.security ?? false + ...(repo.docs.security !== undefined ? { security: repo.docs.security } : {}) } } : {}), diff --git a/src/types.ts b/src/types.ts index 7a4f348..cb1a787 100644 --- a/src/types.ts +++ b/src/types.ts @@ -55,7 +55,7 @@ export interface RepoConfig { docs?: { readme: boolean; contributing: boolean; - security: boolean; + security?: boolean; }; templates?: { pullRequest: "standard" | "none"; diff --git a/tests/render.test.ts b/tests/render.test.ts index dccbb6f..0fac3ce 100644 --- a/tests/render.test.ts +++ b/tests/render.test.ts @@ -205,6 +205,17 @@ describe("renderManagedFiles", () => { expect(renderManagedFiles(optedOutManifest).some((file) => file.path === "SECURITY.md")).toBe(false); }); + it("projects SECURITY.md when public repository docs omit security", () => { + const manifest = normalizeManifest({ + project: { name: "partial-docs-policy", owner: "acme", visibility: "public" }, + repo: { docs: { readme: true } }, + archetype: { kind: "generic-empty" } + }); + + expect(manifest.repo.docs).toEqual({ readme: true, contributing: true }); + expect(renderManagedFiles(manifest).some((file) => file.path === "SECURITY.md")).toBe(true); + }); + it("renders version 2 docs, templates, environment, and workflow switches", () => { const manifest = normalizeManifest({ version: 2,