Skip to content

Security: BenSheridanEdwards/SoftwareFactoryTemplate

Security

.github/SECURITY.md

Security Policy

Supported versions

This is a reference template, not a deployed product. The main branch is the only supported version; adopters fork from it and own their copy thereafter.

Reporting a vulnerability

Please report security issues privately, through a private channel in place of a public issue.

  • Preferred: open a private security advisory from this repository's Security tab (Security → Report a vulnerability).
  • Or email ben@codewalnut.com with the details and a way to reproduce.

You can expect an acknowledgement within a few working days. Once a fix is ready we will coordinate disclosure.

What this repo does to stay secure

These checks are defined in .github/workflows/:

  • Dependency vulnerability audit: pnpm audit --audit-level=high in CI.
  • Secret scanning: gitleaks over history in CI, and gitleaks protect against staged content on pre-commit (when installed locally).
  • Static application security testing: CodeQL on pushes to main, pull requests and a weekly schedule. Public repositories qualify for code scanning. For private repositories, confirm Code Security eligibility and enablement alongside workflow permissions.
  • Action updates: .github/dependabot.yml schedules weekly github-actions updates with a seven-day cooldown. Actions use full commit SHA pins; Dependabot keeps their version comments current. Review updates through the normal gates and human merge process.

Secrets stay out of the repository; the optional E2E_BASE_URL override is documented in playwright.config.ts, and real .env files are git-ignored.

Hosted security and merge enforcement

Onboard a repository

Public repositories qualify for branch protection and code scanning; private repositories need an eligible plan (see below). Configure and verify hosted settings after copying the workflow definitions.

  1. Confirm the destination repository supports branch protection or rulesets. Private repositories need an eligible paid plan. See protected branches.
  2. For private repositories, confirm GitHub Code Security eligibility and enablement before expecting CodeQL results in the Security tab. This is separate from Actions permissions and from branch protection. See CodeQL availability.
  3. Have an authorised maintainer create an active ruleset targeting main, the recommended setup. Require exactly the checks in the generated inventory and its enforcement-approval commit status for the existing human approval policy. Review bypass permissions. Classic branch protection is also supported.
  4. Run pnpm gates:protection after each settings change, from the destination repository with an authenticated gh session. Check a real pull request at its latest head. Confirm the checks run and the platform prevents merging while a required check fails or approval is absent.

Read-only checks (replace OWNER/REPO with your repository):

gh api repos/OWNER/REPO/branches/main/protection
gh api repos/OWNER/REPO/rules/branches/main
gh api repos/OWNER/REPO/code-scanning/alerts
pnpm gates:protection

An upgrade-required response means the platform prerequisite is unmet. An API permission error needs investigation; verify installed protection separately. The command unions ruleset and classic required contexts. A missing source (404 or "Branch not protected") contributes no contexts. Classic protection needs admin read; on 403 the command reports that limit and judges the ruleset contexts. Rulesets need repository read access. Other API errors fail the check. For saved JSON from either endpoint, use node scripts/required-gates.mjs --protection with the JSON on stdin. pnpm gates:check validates repository files only. The generated inventory's "Branch protection" column identifies the intended enforcement mechanism; verify live configuration separately. Until the settings and a failing-check merge refusal have been verified, treat these as reporting checks with a manual merge policy.

The Required gates aggregate covers CI lanes. CodeQL, Lighthouse and PR quality are separate checks. The enforcement-approval status is separate too: its checkbox must be edited by the designated human approver. A tick carries to a new head only while the guarded changes stay identical; tick again after they change. Keep an unavailable security check visibly unresolved.

pnpm store caches

Restore pnpm stores in CI dependency-install jobs using OS, Node version file and lockfile hashes, with fallback keys scoped to the same OS and Node prefix. Keep frozen installs. Set PNPM_STORE_DIR for a persistent self-hosted store to bypass cache restore and save.

CI still does not run on push. The narrow manual-only exception to the no-push CI policy permits pnpm cache saves only from a run the maintainer starts by hand on main with save_pnpm_cache explicitly enabled and no exact hit. The input defaults to false; automated main-tree dispatches leave it unset. Keep PR runs and automated dispatches read-only for pnpm caches. This confines writers to maintainer-dispatched trusted main code. The Fill caches from main workflow (.github/workflows/cache-seed.yml) also saves caches from a push to main that changes a cache key input. Keep pnpm store caches outside privileged workflow_run and pull_request_target workflows.

Secret scanning

CI downloads the MIT-licensed Gitleaks CLI at a pinned version and verifies the release archive against a committed SHA-256 before executing it. The CLI provides the scanner independently of the Gitleaks Action licensing requirements. Checkout retains full history; scripts/secret-scan-ci.sh scans all fetched refs and full history, including secrets subsequently deleted. Both findings and execution failures fail the security job. The same job first runs scripts/secret-scan-ci.test.sh against clean and synthetic-secret Git history.

To reproduce with the workflow's Gitleaks version installed:

bash scripts/secret-scan-ci.test.sh
bash scripts/secret-scan-ci.sh

CodeQL and dependency updates

CodeQL needs actions: read for private workflow-run metadata, contents: read for source and security-events: write for upload. For private repositories, eligibility and enablement of Code Security remain separate prerequisites. Keep analysis and upload enabled; resolve any entitlement error with the repository owner. Local CodeQL CLI use on private code has licensing prerequisites too, so confirm those before local analysis.

Dependabot covers the github-actions ecosystem only. pnpm audit --audit-level=high remains the dependency vulnerability gate. GitHub-hosted Dependabot alerts/security updates and native secret scanning are separate repository settings to assess with the owner.

In pull_request_target, issue_comment and workflow_run workflows, pass untrusted input through github-script's context or through env. Keep ${{ }} expressions outside run and script bodies. scripts/action-pins.test.ts checks this rule, the SHA pins, cleared cache/download commits and unchanged workflow permissions.

Pact contract testing

The 31 October 2026 clearance covers @pact-foundation/pact 17.1.4, @pact-foundation/pact-core 20.2.0, its seven optional platform packages at 20.2.0, and @scarf/scarf 1.4.0 with its build denied. Pin Pact exactly and override Core to 20.2.0. Core's install script is empty and the optional platform packages supply prebuilt binaries; keep Core outside allowBuilds.

Keep '@scarf/scarf': false in allowBuilds and scarfSettings.enabled false. Set SCARF_ANALYTICS=false and PACT_DO_NOT_TRACK=true in local contract commands and CI. Use no Pact plugins, leave PACT_PREBUILD_LOCATION and PACT_PREBUILD_PACKAGE_LOCATION unset, retain the release-age floor and use frozen lockfile installs. Run pnpm audit --audit-level=high after dependency changes and permit no new advisory IDs against main. The approved follow-redirects 1.16.2 override fixes its high-severity advisory.

There aren't any published security advisories