diff --git a/AGENTS.md b/AGENTS.md index cf81b94..a40edeb 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -32,8 +32,13 @@ repositories pin by full commit SHA. Docs under `docs/` are human mirrors; - Tier claims (public / private-free / private-paid) must match the real GitHub billing contract. Known plan gate: GitHub Artifact Attestations are public-only on Free/Pro/Team plans; private/internal repositories require - GitHub Enterprise Cloud (GHAS/Code Security does not unlock them). GitHub - Code Quality becomes a paid product on 2026-07-20 — refresh that fact then. + GitHub Enterprise Cloud (GHAS/Code Security does not unlock them). Second + known gate: GitHub Code Quality (GA and billable since 2026-07-20) is **not** + sorted by visibility — it is billed per active committer on public repos too, + on a licence independent of GHAS, with committers counted once per + organization. It is a platform feature with no REST/GraphQL API, so it carries + `workflow: null` / `example: null` and lives in its own tier doc + (`docs/16-code-quality.md`); never fold it into the public tier. ## CI skills diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d2d08b..c6c4399 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,75 @@ ## [Unreleased] +### Added + +- **GitHub Code Quality is now a modelled tier instead of an unattached price + tag.** The product existed in the ledger as `github-code-quality-transition` + and nowhere else: no capability, no tier doc, no mention in the tier tables — + so the catalog priced a product it never told adopters how to place. It gets + `catalog/capabilities.yml` entry `github-code-quality` and a tier doc, + `docs/16-code-quality.md`. + + It is deliberately **not** folded into the public tier. Re-verification + against the billing docs on 2026-08-01 established that visibility does not + gate the licence: a public repository is billed the same per-active-committer + rate as a private one, and being public only removes the Actions-minutes + component. That contradicted `docs/01-public-oss-free.md`, which promised the + "entire security and supply-chain suite for free" — a claim that would have + been false for any adopter who enabled Code Quality on a public repo. The + three-tier model sorts by visibility and plan; this product obeys neither, so + it is documented as an orthogonal fourth tier and **excluded** from both free + tiers, with the free maintainability substitutes (`coverage-gate.yml`, + `docs-quality.yml`, `pr-hygiene.yml`, zizmor, the language packs) named + explicitly. + + Two further billing facts are now recorded because they invert the usual + cost-control instinct: committers are counted **once per organization**, so + enabling one repository already bills the whole active-committer set and a + "few paid repos, many free repos" split saves nothing unless the committer + sets actually differ; and the licence is independent of Code Security and + Secret Protection, so GHAS does not include it and holding both means paying + two products to drive one CodeQL engine. + + The capability carries `workflow: null` and `example: null`, which is the + honest shape rather than a gap: Code Quality has no Action, no `workflow_call` + entrypoint, and no REST or GraphQL API, so enablement is UI-only and cannot be + pinned by SHA, asserted, or drift-checked from CI. For the same reason the + merge gate — ruleset rule "Require code quality results", severity threshold, + check `CodeQL - Code Quality` — is documented as a UI procedure and **not** + encoded in `.github/rulesets/`: those specs are shaped for + `POST /repos/{owner}/{repo}/rulesets` and the rule-type identifier for this + rule is undocumented. `AGENTS.md` records the gate so the next contributor does + not re-derive it, replacing a stale instruction to "refresh that fact" on a + date that has passed. + +- **The library described GitHub's price list, not this estate's receipts.** + Every tier doc reasoned from what GitHub charges a hypothetical adopter, so a + private NDDev repository was configured as if it were on the free plan while + the organization was already paying for Enterprise Cloud, Code Security, + Secret Protection, and Code Quality. `docs/17-nddev-tier.md` records the + verified entitlements and the advice that consequently does not apply. + + The concrete loss this closes: `docs/02-private-free.md` routes private + releases to `release-supply-chain-free.yml` because Artifact Attestations + require Enterprise Cloud on private repos. This estate **has** Enterprise + Cloud, so all 26 private repositories were emitting `slsa_build_level: null` + and discarding provenance that was already bought. They can use the attested + `release-supply-chain.yml`. + + Also recorded, because it inverts the usual cost instinct: these products bill + per active committer counted **once per organization**, so with one committer + the estate pays the same whether one repository or fifty are enabled — partial + coverage would have cost identically and protected less. `examples/nddev/` + joins the aggregate-example allowlist in `validate_catalog.py`, alongside the + three existing per-tier security suites. + + The doc is explicit about what is *absent* too: Copilot Autofix is unavailable + (Copilot Business provisioned, zero seats assigned), and SHA pinning is + unenforced at both org and enterprise (`sha_pinning_required: false`) — flagged + rather than recommended blindly, since enabling it org-wide would break any + repository still pinning actions by tag. + ### Fixed - **A grouped action bump could not land on its own.** Dependabot updates the diff --git a/README.md b/README.md index 6404376..0334076 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,8 @@ A **July-2026 GitHub-native CI/CD, security, governance, and supply-chain automation knowledge base plus reusable workflow library** for the NDDev estate. It separates three billing realities — **public OSS**, **private-free**, and -**private-paid/GHAS** — ships SHA-pinned reusable workflows for each, and +**private-paid/GHAS** — plus one opt-in product that ignores all three +(**Code Quality**), ships SHA-pinned reusable workflows for each, and documents every capability with its status, cost model, risk, and implementation path in [`docs/`](docs/00-overview.md) and the machine-readable [`catalog/`](catalog/README.md). @@ -21,6 +22,21 @@ See [`docs/01-public-oss-free.md`](docs/01-public-oss-free.md), [`docs/02-private-free.md`](docs/02-private-free.md), and [`docs/03-private-paid-ghas.md`](docs/03-private-paid-ghas.md). +**GitHub Code Quality sits outside these three.** It is billed per active +committer at *any* visibility — public repositories are **not** free — on a +licence GHAS does not include, and committers are counted once per organization +rather than per repository. Both free tiers exclude it; enable it deliberately +via the org-level **Repository access** control. See +[`docs/16-code-quality.md`](docs/16-code-quality.md). + +**The NDDev estate is not on the free plan.** The three tiers above describe what +GitHub offers; [`docs/17-nddev-tier.md`](docs/17-nddev-tier.md) records what this +organization has already bought — Enterprise Cloud, Code Security, Secret +Protection, Code Quality — and which generic tier advice therefore does **not** +apply here. Chiefly: private repositories release with attested +`release-supply-chain.yml`, not the `-free` variant, because Artifact +Attestations are gated on Enterprise Cloud and this estate has it. + ## Capability → workflow map | Capability | Workflow | Public | Private-free | Private-paid | diff --git a/catalog/capabilities.yml b/catalog/capabilities.yml index ba4e565..8722db1 100644 --- a/catalog/capabilities.yml +++ b/catalog/capabilities.yml @@ -34,6 +34,38 @@ capabilities: - "https://docs.github.com/en/code-security/code-scanning/introduction-to-code-scanning/about-code-scanning-with-codeql" - "https://github.com/github/codeql-action" + - id: github-code-quality + name: GitHub Code Quality + product_facts: + - github-code-quality-transition + cluster: security-scanning + status: ga + public_oss: paid + private_free: paid + private_paid: conditional + workflow: null + example: null + required_permissions: [] + required_settings: + - "Team or Enterprise Cloud plan" + - "Enterprise owner allows Code Quality at the enterprise level" + - "Organization Settings -> Security -> Code quality -> Repository access scoped to the paid-tier repositories" + - "GitHub Actions enabled on the repository (scans run as Actions workflows)" + - "Optional PR gate: ruleset rule 'Require code quality results' with a severity threshold" + risks: + - "Billed per active committer on public repositories too; being public does not make it free the way CodeQL and GHAS are" + - "Committers are counted once per organization, so enabling a single repository already bills the whole active committer set" + - "Scans consume Actions minutes on private repositories; AI-assisted detection and Autofix consume AI credits" + - "Platform feature with no REST or GraphQL API: enable and disable are UI-only and cannot be asserted from CI" + - "Requiring code quality results blocks merges while analysis is still running or when it fails, e.g. on exhausted Actions minutes" + deprecations: null + last_verified: "2026-08-01" + sources: + - "https://docs.github.com/en/code-security/concepts/about-code-quality" + - "https://docs.github.com/en/code-security/how-tos/maintain-quality-code/enable-code-quality" + - "https://docs.github.com/en/code-security/how-tos/maintain-quality-code/set-pr-thresholds" + - "https://docs.github.com/en/billing/concepts/product-billing/github-code-quality" + - id: ossf-scorecard name: OpenSSF Scorecard SARIF cluster: security-scanning diff --git a/catalog/product-facts.yml b/catalog/product-facts.yml index 435c663..cf1e0ca 100644 --- a/catalog/product-facts.yml +++ b/catalog/product-facts.yml @@ -499,21 +499,29 @@ facts: conditions: - Public preview before 2026-07-20 - GA and paid from 2026-07-20 + - Repository visibility does not discount the licence; public repositories bill the same per-active-committer rate as private + - Active committers are counted once per organization across every repository with Code Quality enabled + - A committer is active if one of their commits was pushed in the last 90 days, regardless of authoring date + - Bot accounts are not billed + - Licence is independent of Code Security / Secret Protection; Advanced Security does not include it - AI capabilities add usage charges - Deterministic CodeQL analysis consumes Actions minutes - - Disable before 2026-07-20 to avoid paid enablement if not wanted + - Scope with the org-level Repository access control (all / selected / no repositories) to choose which repositories bill payment_method: required-from-2026-07-20 overage: AI usage and Actions minutes may add cost status: official source_authority: primary - verified_at: '2026-07-21' + verified_at: '2026-08-01' expires_after: '2026-10-21' source_urls: - https://github.blog/changelog/2026-06-16-github-code-quality-generally-available-july-20-2026/ - https://docs.github.com/en/billing/concepts/product-billing/github-code-quality + - https://docs.github.com/en/code-security/how-tos/maintain-quality-code/enable-code-quality + - https://docs.github.com/en/code-security/how-tos/maintain-quality-code/disable-code-quality notes: - 'GA + paid transition completed 2026-07-20 as scheduled; no longer time-critical.' - 'Re-verified 2026-07-21 against the billing docs: standalone per-active-committer license ($10/active committer/month), CodeQL scans consume Actions minutes, AI features consume AI credits at $0.01/credit, independent of Advanced Security licenses; Team + Enterprise Cloud.' + - 'Re-verified 2026-08-01: visibility does not gate the licence. Unlike CodeQL/GHAS, a public repository does NOT make Code Quality free — public repos only avoid the Actions-minutes component (standard runners are unmetered on public). This is why the tier docs exclude Code Quality from both free tiers instead of folding it into the public tier.' - id: gitlab-free-hosted provider: GitLab product: GitLab.com Free compute diff --git a/docs/00-overview.md b/docs/00-overview.md index 5898a7f..1a311cc 100644 --- a/docs/00-overview.md +++ b/docs/00-overview.md @@ -27,6 +27,26 @@ Every doc here maps features to one of three tiers. | **Private-free (zero-cost only)** | actionlint, zizmor (no SARIF upload), gitleaks, static validation, SBOM + checksummed immutable releases (attestations need GHEC on private), OIDC, cross-platform smoke. CodeQL / native secret scanning / dependency review / harden-runner are **paid** and excluded | [02-private-free.md](02-private-free.md) | | **Private-paid / GHAS** | Code scanning, secret scanning + push protection, dependency review, Copilot Autofix, delegated governance — requires paid GitHub Code Security / Secret Protection | [03-private-paid-ghas.md](03-private-paid-ghas.md) | +### The one capability that ignores the model + +**GitHub Code Quality** (GA and billable since 2026-07-20) is **not** sorted by +visibility: it is billed per active committer on public repositories exactly as +on private ones, its licence is independent of GHAS, and committers are counted +once per organization rather than per repository. It therefore sits outside the +three tiers as an opt-in fourth one — see +[16 Code Quality tier](16-code-quality.md). Both free tiers exclude it, and the +free maintainability substitutes are listed there. + +### What this estate actually owns + +The three tiers describe what GitHub *offers*. [17 NDDev estate +tier](17-nddev-tier.md) records what the NDDev-it-com organization has **already +bought** — Enterprise Cloud, Code Security, Secret Protection, and Code Quality — +so repositories here stop being configured as if they were on the free plan. Its +most consequential correction: Artifact Attestations are gated on Enterprise +Cloud for private repos, and this estate **has** it, so private repositories +release with full provenance instead of the `-free` variant. + ## How to consume a reusable workflow Reference by `owner/repo/.github/workflows/.yml@` from a caller @@ -84,7 +104,9 @@ For end-to-end caller examples per tier, see the tier docs and the repository - Tiers: [01 Public OSS free](01-public-oss-free.md) · [02 Private free](02-private-free.md) · - [03 Private paid / GHAS](03-private-paid-ghas.md) + [03 Private paid / GHAS](03-private-paid-ghas.md) · + [16 Code Quality](16-code-quality.md) · + [17 NDDev estate](17-nddev-tier.md) - Platform: [04 Actions core](04-actions-core.md) · [05 Runners](05-runners.md) - Security: [06 Security scanning](06-security-scanning.md) · [07 Supply chain / SLSA / SBOM / attestations](07-supply-chain-slsa-sbom-attestations.md) diff --git a/docs/01-public-oss-free.md b/docs/01-public-oss-free.md index 21c7e90..207790d 100644 --- a/docs/01-public-oss-free.md +++ b/docs/01-public-oss-free.md @@ -5,6 +5,12 @@ for free**, including features that are paid on private repositories. This is th tier the NDDev estate targets for open-source work, and it is where the library delivers its full value. +> **One documented exception: GitHub Code Quality is not free on public repos.** +> Unlike CodeQL and everything else in the table below, it is billed per active +> committer regardless of visibility. Being public only makes its Actions minutes +> free. Keep it out of this tier — see +> [16 Code Quality tier](16-code-quality.md). + ## What public OSS gets free | Capability | Free on public? | Delivered by | @@ -19,6 +25,7 @@ delivers its full value. | GitHub Pages | ✅ | [10 Deployments & environments](10-deployments-environments.md) | | Repository rulesets | ✅ | [08 Governance & rulesets](08-governance-rulesets.md) | | harden-runner egress control | ✅ | explicit public/GHAS workflows | +| GitHub Code Quality | ❌ paid per active committer even on public | [16 Code Quality tier](16-code-quality.md) | > Standard GitHub-hosted runners are free with unlimited minutes on public > repositories. Larger, GPU, and macOS-XL runners are billable **even for diff --git a/docs/02-private-free.md b/docs/02-private-free.md index cfe12e2..4964bf7 100644 --- a/docs/02-private-free.md +++ b/docs/02-private-free.md @@ -19,6 +19,13 @@ them in the private-free tier: | `step-security/harden-runner` | Paid on private repos | use a private-free workflow with no action reference | | SARIF upload to code scanning | Requires code scanning (paid) | use the no-SARIF workflow variant | | GitHub Artifact Attestations | Require GitHub Enterprise Cloud on private/internal (a plan gate — GHAS does not unlock it) | use `release-supply-chain-free.yml` (below) | +| GitHub Code Quality | Billed per active committer at **any** visibility, on a licence GHAS does not include | [16 Code Quality tier](16-code-quality.md) | + +> Code Quality is the one exclusion here that publishing the repository does +> **not** solve: it is paid on public repos too. Keeping this tier free means +> leaving the org-level **Repository access** control off this repository — the +> free maintainability substitutes are listed in +> [16 Code Quality tier](16-code-quality.md#what-the-free-tiers-do-instead). ## The zero-cost private stack diff --git a/docs/03-private-paid-ghas.md b/docs/03-private-paid-ghas.md index 041dc34..f54e310 100644 --- a/docs/03-private-paid-ghas.md +++ b/docs/03-private-paid-ghas.md @@ -35,6 +35,14 @@ turning it on for private repos, where it is billed per active committer. > `release-supply-chain-free.yml` instead > (see [07 Supply chain](07-supply-chain-slsa-sbom-attestations.md)). +> **Also not unlocked by GHAS:** GitHub Code Quality is a **separate licence**, +> billed per active committer independently of Code Security and Secret +> Protection. Buying either GHAS product does not include it, and buying Code +> Quality does not give you CodeQL *security* scanning on a private repo. Both +> drive CodeQL, so holding both means paying two products to run one engine over +> the same code for different query suites — +> see [16 Code Quality tier](16-code-quality.md#relationship-to-ghas). + ## Enabling on a private repo 1. Purchase/assign GHAS (Code Security and/or Secret Protection) at the org or diff --git a/docs/16-code-quality.md b/docs/16-code-quality.md new file mode 100644 index 0000000..1af641f --- /dev/null +++ b/docs/16-code-quality.md @@ -0,0 +1,121 @@ +# Code Quality tier — paid per active committer, at any visibility + +GitHub Code Quality is a **maintainability** product: it runs CodeQL quality +queries (not only security queries) and reports findings, quality scores, and +history, with optional AI-assisted detection and Copilot Autofix. It went GA and +became billable on **2026-07-20**. + +It gets its own tier because it **breaks the visibility rule the other three +tiers are built on**. + +## Why this is a separate tier + +The [three-tier model](00-overview.md#the-three-tier-model) sorts capabilities by +repository visibility and plan: public is free, private-free is the zero-cost +subset, private-paid unlocks the rest through GHAS. Code Quality obeys none of +that: + +| Assumption that holds for CodeQL / GHAS | What Code Quality actually does | +| --- | --- | +| Public repositories get it free | **No.** Public repos are billed the same per-active-committer rate as private | +| A paid GHAS licence unlocks it | **No.** The licence is independent of Code Security and Secret Protection | +| Cost scales with the repositories you enable | **No.** Committers are counted **once per organization** | + +The only thing being public saves is the **Actions-minutes** component: scans run +as Actions workflows, and standard runners are unmetered on public repositories. +The per-committer licence is unchanged. + +The consequence for cost control is blunt: **enabling Code Quality on one +repository already bills your entire active-committer set.** Splitting an estate +into "a few Code Quality repos and many free repos" saves nothing unless the +people who commit to the paid repos are a strictly smaller group than the people +who commit anywhere. Scope the tier by **who commits**, not by how many repos. + +Live price, plan, and committer-counting rules are in the fact ledger, not in +this prose: [`github-code-quality-transition`](generated/free-tier-matrix.md). + +## What the free tiers do instead + +Both free tiers **exclude** Code Quality and get their maintainability signal +from workflows in this library, which cost nothing beyond Actions minutes: + +| Need | Free substitute | Tier | +| --- | --- | --- | +| Security-focused static analysis | `public-codeql.yml` | [01 Public OSS](01-public-oss-free.md) (free on public) | +| Actions static analysis | `zizmor-sarif.yml` / `zizmor-no-sarif.yml` | both | +| Coverage threshold gate | `coverage-gate.yml` | both | +| Lint / type / build packs | the language packs in [15 Language & quality packs](15-language-and-quality-packs.md) | both | +| Docs quality | `docs-quality.yml` | both | +| PR hygiene | `pr-hygiene.yml` | both | + +Those are not a feature-equivalent replacement — they do not produce Code +Quality's maintainability scores or its history — but they keep the free tiers +genuinely free. + +## Enabling the tier + +Code Quality is a **platform feature, not a reusable workflow.** This library +ships no caller for it, and there is nothing to pin by SHA: it has no Action, no +`workflow_call` entrypoint, and **no REST or GraphQL API**. Enablement is UI-only +and therefore cannot be asserted, drift-checked, or rolled back from CI — which +is why its catalog entry carries `workflow: null` and `example: null`. + +1. **Enterprise** — an enterprise owner must allow Code Quality at the + enterprise level, or the org setting has no effect. +2. **Organization** — Settings → Security → **Code quality** → **Repository + access**. This dropdown *is* the tier boundary: + - `No repositories` — the whole org stays in the free tiers. + - `Selected repositories` — the Code Quality tier; pick them explicitly. + - `All repositories` — every repo joins the paid tier, including public ones. + Optionally set **Enforce access** so repository admins cannot opt themselves + back in. +3. **Repository** — Settings → Security → **Code quality** → *Enable code + quality*, then choose analysed languages and runner type. Actions must be + enabled on the repo. + +Prefer `Selected repositories` + `Enforce access`: `All repositories` silently +pulls every public repo into a paid product. + +## Using it as a merge gate + +Enablement only produces findings. To make it block a bad merge, add the ruleset +rule on the target branch: + +- Repository or org ruleset → **Branch rules** → **Require code quality + results**. +- **Severity** selects the lowest severity that must be resolved before merge: + `Errors`, `Warnings and higher`, `Notes and higher`, or `All`. +- The check that must report success is **`CodeQL - Code Quality`**. + +The rule also blocks merges while analysis is **still running** or when it +**failed** — including failure caused by exhausted Actions minutes. On a private +repo with a tight minutes budget that turns a billing problem into a merge +outage; start at `Errors` and widen once the lane is proven. + +> The REST ruleset rule-type identifier for this rule is not documented, so the +> repo's [`.github/rulesets/`](../.github/rulesets/) specs — which are shaped for +> `POST /repos/{owner}/{repo}/rulesets` and validated by +> `scripts/check_rulesets.py` — do not encode it. Configure this rule in the UI +> and treat the JSON specs as covering only the API-expressible rules. See +> [08 Governance & rulesets](08-governance-rulesets.md). + +## Relationship to GHAS + +Code Quality is **not** part of GitHub Advanced Security. A repository can hold +any combination of Code Security, Secret Protection, and Code Quality licences; +buying [03 Private-paid / GHAS](03-private-paid-ghas.md) does not include it, and +buying Code Quality does not unlock CodeQL *security* scanning on a private repo. + +Both run CodeQL, so on a private repo with both licences you are paying two +products to drive one engine over the same code for different query suites, and +both consume Actions minutes. + +## Turning the tier off + +Set organization **Repository access** to `No repositories`, or toggle the +repository switch off. Scans and the billing they generate stop immediately; +usage already accrued in the current cycle still bills. Findings, scores, and +history are retained and return if you re-enable — disabling is not data loss. + +--- +Last verified: 2026-08-01 diff --git a/docs/17-nddev-tier.md b/docs/17-nddev-tier.md new file mode 100644 index 0000000..05c4b3b --- /dev/null +++ b/docs/17-nddev-tier.md @@ -0,0 +1,111 @@ +# NDDev estate tier — everything the organization already pays for + +The other four tier docs describe what GitHub *offers* at a given visibility and +plan. This one describes what the **NDDev-it-com organization actually owns**, +so that a repository in this estate stops being configured as if it were on the +free plan. + +Entitlements, verified 2026-08-01 against the enterprise licensing page: + +| Product | Licences | Consequence for this library | +| --- | --- | --- | +| **Enterprise Cloud** | 1 consumed | Artifact Attestations work on **private** repos | +| **Code Security** | 1 active committer, 23 repos | CodeQL + SARIF upload legal on private | +| **Secret Protection** | 1 active committer, 26 repos | Native secret scanning on private (push protection deliberately off) | +| **Code Quality** | 1 consumed | Maintainability scans + PR gate — see [16](16-code-quality.md) | + +Platform side is already applied: the org security configuration +**`nddev-config`** (`enforcement: enforced`) is attached to all 50 repositories +and is the default for new ones. It enables GHAS, secret scanning, validity +checks, non-provider patterns, Dependabot security updates, and private +vulnerability reporting. + +Two settings are deliberately **not** in it, and both decisions are load-bearing: + +- **`secret_scanning_push_protection: disabled`.** Push protection is the only + control that stops a secret reaching the remote at all; with it off, detection + is after the fact and the remedy is rotation, not prevention. It was turned off + as an explicit velocity trade-off. Treat a secret-scanning alert here as an + already-leaked credential. +- **`code_scanning_default_setup: not_set`.** Config attachment is atomic per + repository: forcing default setup where an active CodeQL *advanced* setup + exists fails the **whole** attachment, taking secret scanning down with it — + observed exactly once, when the stock `GitHub recommended` configuration + attached to 4 of 24 public repos and failed on the 20 running `codeql.yml`. + Code scanning is therefore enabled per repository instead, which is why + coverage is **50/50**: 30 repositories on default setup, 20 on their own + CodeQL workflow. + +## The correction this tier exists to make + +[02 Private free](02-private-free.md) tells a private repository to release with +`release-supply-chain-free.yml`, because Artifact Attestations require GitHub +Enterprise Cloud on private and internal repos. + +**That gate does not apply here — this estate has Enterprise Cloud.** Every one +of the 26 private repositories can use the attested `release-supply-chain.yml` +and get SLSA build provenance. Using the `-free` variant here throws away +provenance that is already paid for. + +The same inversion applies across the board: in the generic model a private repo +is the *degraded* case. In this estate it is not. Private and public repositories +run the **same** callers — the difference is only who pays, and that is settled. + +| Capability | Generic private-free | This estate | +| --- | --- | --- | +| CodeQL + SARIF | excluded (paid) | `public-codeql.yml`, `zizmor-sarif.yml` | +| Native secret scanning | gitleaks substitute | native, plus `secret-scan.yml` for history | +| Push protection | unavailable (paid) | licensed but **off** by choice — see above | +| Dependency review | excluded (paid) | `public-dependency-review.yml` | +| Release provenance | `release-supply-chain-free.yml` | **`release-supply-chain.yml`** (attested) | +| Maintainability | lint/coverage only | Code Quality + the free packs | + +## Callers + +Security suite: [`examples/nddev/security.yml`](../examples/nddev/security.yml). +It is the private-paid/GHAS suite plus `osv-scan.yml`, and it runs unchanged on +public repositories. + +Release with provenance — on **private** repos too: + +```yaml +jobs: + publish: + permissions: + contents: write + id-token: write + attestations: write + artifact-metadata: write + uses: NDDev-it-com/ci-workflows/.github/workflows/release-supply-chain.yml@ +``` + +The manifest records `slsa_build_level: 3` rather than the `null` that +`release-supply-chain-free.yml` writes. See +[07 Supply chain](07-supply-chain-slsa-sbom-attestations.md). + +## What this estate does *not* have + +- **Copilot Autofix.** Copilot Business is provisioned on the organization but + **zero seats are assigned** (`seat_management_setting: unconfigured`), so + Autofix on code scanning and Code Quality findings is unavailable until a seat + is assigned. That is a per-seat paid product; the $10 Code Quality licence does + not include it. +- **SAML SSO, IP allow list, SSH certificate authorities.** None configured at + org or enterprise level. +- **Enforced SHA pinning.** Both org and enterprise report + `sha_pinning_required: false`. This library enforces full-SHA pins on itself + through `scripts/check_pinned_actions.py`, but the platform does not enforce it + on other repositories in the estate. Turning it on org-wide would break any + repository still referencing actions by tag — audit before enabling. + +## Cost note that governs tier choice + +Every paid product here — Code Security, Secret Protection, Code Quality — bills +per **active committer**, counted **once per organization**, not per repository. +With a single active committer the estate pays the same whether one repository or +all fifty are enabled. That is why `nddev-config` is attached to all 50 rather +than a chosen subset: partial coverage would have cost exactly the same and +protected less. Re-evaluate that reasoning the moment a second committer joins. + +--- +Last verified: 2026-08-01 diff --git a/docs/generated/capability-matrix.md b/docs/generated/capability-matrix.md index 9b0da23..5833be3 100644 --- a/docs/generated/capability-matrix.md +++ b/docs/generated/capability-matrix.md @@ -54,6 +54,7 @@ | Container build and Trivy scan (`container-scan-trivy`) | security-scanning | ga | free | free | available | `.github/workflows/container-ci.yml` | `examples/infra/container.yml` | | Copilot Autofix for code scanning (`copilot-autofix`) | security-scanning | ga | free | unavailable | available | `-` | `-` | | Fuzzing (`fuzzing`) | security-scanning | ga | free | conditional | available | `.github/workflows/fuzzing.yml` | `examples/testing/fuzzing.yml` | +| GitHub Code Quality (`github-code-quality`) | security-scanning | ga | paid | paid | conditional | `-` | `-` | | Gitleaks secret scan (`gitleaks-secret-scan`) | security-scanning | ga | free | free | available | `.github/workflows/secret-scan.yml` | `-` | | hadolint Dockerfile lint (`hadolint-dockerfile`) | security-scanning | ga | free | free | available | `.github/workflows/hadolint-ci.yml` | `examples/security/hadolint.yml` | | IaC scan (Checkov) (`iac-scan-checkov`) | security-scanning | ga | free | free | available | `.github/workflows/iac-scan.yml` | `examples/security/iac-scan.yml` | diff --git a/docs/generated/free-tier-matrix.md b/docs/generated/free-tier-matrix.md index e5aa36c..99849e7 100644 --- a/docs/generated/free-tier-matrix.md +++ b/docs/generated/free-tier-matrix.md @@ -29,7 +29,7 @@ past its expiry, so a stale tier claim cannot silently mislead adopters. | `github-actions-self-hosted-control-plane` | GitHub | Actions self-hosted runners | public, private, internal | All eligible GitHub plans | zero-license-control-plane | unmetered (github-actions-control-plane-minutes) | official | 2026-07-11 | 2026-08-10 | | `github-attestations-private` | GitHub | Artifact attestations | private, internal | Enterprise Cloud | plan-gated | attestations | official | 2026-07-11 | 2026-08-10 | | `github-attestations-public` | GitHub | Artifact attestations | public | Free, Pro, Team, Enterprise Cloud | included-feature | attestations | official | 2026-07-11 | 2026-08-10 | -| `github-code-quality-transition` | GitHub | Code Quality | public, private, internal | Team, Enterprise Cloud | scheduled-paid-transition | 10 usd-per-active-committer/month | official | 2026-07-21 | 2026-10-21 | +| `github-code-quality-transition` | GitHub | Code Quality | public, private, internal | Team, Enterprise Cloud | scheduled-paid-transition | 10 usd-per-active-committer/month | official | 2026-08-01 | 2026-10-21 | | `github-dependabot-private` | GitHub | Dependabot alerts and security updates | private | All eligible plans | included-feature | repository-feature | official | 2026-07-11 | 2026-08-10 | | `github-dependency-review-private` | GitHub | Dependency review | private | Team, Enterprise Cloud | add-on-gated | repository-feature | official | 2026-07-11 | 2026-08-10 | | `github-dependency-review-public` | GitHub | Dependency review | public | All current plans | included-feature | repository-feature | official | 2026-07-11 | 2026-08-10 | diff --git a/examples/nddev/security.yml b/examples/nddev/security.yml new file mode 100644 index 0000000..11a7319 --- /dev/null +++ b/examples/nddev/security.yml @@ -0,0 +1,34 @@ +# NDDev estate tier — the full paid stack the NDDev-it-com organization actually owns: +# Enterprise Cloud + Code Security + Secret Protection + Code Quality. +# Runs identically on public and private repositories; on private it is the +# GHAS licences that make CodeQL and SARIF upload legal, not the visibility. +# Replace @ with a pinned full commit SHA of ci-workflows. +name: security +on: + push: { branches: [main] } + pull_request: { branches: [main] } +permissions: {} +concurrency: + group: security-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true +jobs: + codeql: + permissions: { actions: read, contents: read, security-events: write } + uses: NDDev-it-com/ci-workflows/.github/workflows/public-codeql.yml@ + with: + languages: '["python","actions"]' + secret-scan: + permissions: { contents: read } + uses: NDDev-it-com/ci-workflows/.github/workflows/secret-scan.yml@ + actionlint: + permissions: { contents: read } + uses: NDDev-it-com/ci-workflows/.github/workflows/actionlint.yml@ + zizmor: + permissions: { contents: read, security-events: write } + uses: NDDev-it-com/ci-workflows/.github/workflows/zizmor-sarif.yml@ + dependency-review: + permissions: { contents: read, pull-requests: write } + uses: NDDev-it-com/ci-workflows/.github/workflows/public-dependency-review.yml@ + osv-scan: + permissions: { contents: read } + uses: NDDev-it-com/ci-workflows/.github/workflows/osv-scan.yml@ diff --git a/scripts/validate_catalog.py b/scripts/validate_catalog.py index 932a037..d81d03d 100644 --- a/scripts/validate_catalog.py +++ b/scripts/validate_catalog.py @@ -139,6 +139,7 @@ def check() -> list[str]: "examples/public-oss/security.yml", "examples/private-free/security.yml", "examples/private-paid-ghas/security.yml", + "examples/nddev/security.yml", } missing_examples -= allowed_aggregate_examples if missing_examples: