Compare commits

...
7 Commits
Author SHA1 Message Date
koftwentytwo-standards-releases[bot]andCodex 7c69368443 chore(main): release 1.0.0 (#20)
Publish the stable standards baseline with mandatory applicable badge participation
and parser fuzzing, the passing assessment, professional release text, scoped
frontend audit exception, Windows signing correction and readiness documentation.

Release checklist: https://github.com/KofTwentyTwo/standards/issues/30

Validated full conformance, Markdown and links, package regressions, required PR
gates and both 10000-case fuzz campaigns. Existing Windows consumer limitations
remain documented in issue #22 and docs/releases.md.

Signed-off-by: James Maes <james@kof22.com>
Co-Authored-By: Codex (GPT-6) <noreply@openai.com>
2026-10-10 16:22:50 -05:00
james.maesandClaude Fable 5.1 40ecebae5c ci(release-velopack): sign only unsigned files; verify before packing (#29)
## What

First real `sign: true` run (gclo `v1.0.2-beta.2`, run 38080813985):
OIDC login worked, signing started, and the step died after 320 s
because it signs **every** exe/dll under `publish` (~285 in a
self-contained publish, 239 already signed by Microsoft/.NET) and
`azure/artifact-signing-action` stops at its 300 s default `timeout`.

- **Select the binaries to sign**: only files whose
`Get-AuthenticodeSignature` status is not `Valid` go to the action's
`files` input; fails if the main exe (or the CLI exe) is not among them.
Third-party signatures are preserved, nothing is signed twice. `timeout:
1800`.
- **vpk**: its built-in `--azureTrustedSignFile` path hung >10 min on
nine files locally (it skips signed files itself, so the count was never
the issue). vpk now calls a `--signTemplate` wrapper (written into
`RUNNER_TEMP`) that uses the SignTool and the
`Microsoft.ArtifactSigning.Client` dlib the action installed, with the
Azure CLI credential, and skips already-signed files.
- **Verify before packaging further**: `signtool verify /pa /all` on
Setup.exe and every exe/dll inside the portable zip; nothing unsigned
reaches the draft release (K22-REL-05).

Closes #26.

## How it was tested

Reproduced the package job locally with gclo's real publish output
against the live `kof22signing` account (`releases` profile): 48 of 287
binaries selected and signed in 192 s, all verify (Valid, timestamped,
CN=James Maes); `vpk pack` with the wrapper: 9 files + Setup.exe signed,
36 s; Setup.exe, Update.exe, gclo.exe and gclo_ExecutionStub.exe valid;
282 DLLs in the portable zip, 0 unsigned (45 ours, the rest Microsoft's
untouched); CLI exe valid. `actionlint` clean. First CI run will be gclo
`v1.0.2-beta.3` after re-pinning.

Generated with [Claude Code](https://claude.com/claude-code)

Signed-off-by: James Maes <james@kof22.com>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-10 16:07:10 -05:00
james.maesandGPT-6 5c21fc7771 docs(exceptions): scope temporary frontend audit exception (#27)
Website-Frontend CI currently fails on GHSA-vfj7-8cjw-p6xm in
development-only braces 3.0.3. No patched package release exists. Record
EX-0008 for this advisory and its propagated findings only, expiring
November 10, 2026, with a review on October 17.

The implementation retains the full audit, rejects all production
high/critical findings and all critical findings, checks exact locked
development paths/version, fails on audit errors or expiry, and requires
reassessment for any newer braces release. Lint, tests, build, secret
scanning, SAST and Trivy remain enabled. No global scanner suppression
is added.

Validation: Markdown lint and offline link/fragment checks pass. The
consumer guard passes against the current website lockfile and
regression cases for acceptance, expiry, unrelated/mixed advisories,
runtime paths, critical findings, new releases and audit errors. This
register change is the approval record required by the exception policy;
consumer release remains gated by its normal CI.

Signed-off-by: James Maes <james@kof22.com>
Co-authored-by: GPT-6 <codex@openai.com>
2026-10-10 15:09:28 -05:00
james.maesandCodex 83134656fc docs(assurance): record OpenSSF passing badge (#25)
The README and assurance records still say enrollment is blocked, even
though OpenSSF project 15366 now identifies KofTwentyTwo/standards and
displays a passing badge. Add the provider's live badge and project URL,
record the completed maintainer self-assessment, and close the
enrollment-only exception EX-0007 while preserving its history.

Set the next assessment review to 2027-01-08 or before the next
MINOR/MAJOR release, whichever occurs first. Retain the unmet suggested
coverage and warning criteria and the release smoke-test follow-up in
#22. Update Scorecard dispositions without claiming an unobserved score
change.

Validation: public assessment identity and passing status verified;
Markdown lint passed for all 69 documents; offline links and anchors
passed with zero errors; full repository conformance passed with 32
PASS, 5 REVIEW, 3 existing WARN, 1 N/A, and no FAIL/UNKNOWN; git diff
--check passed.

AI assistance: Codex (GPT-6) prepared and validated this documentation
change. No standards requirement, workflow, root AGENTS.md, release tag,
or published asset changes.

Signed-off-by: James Maes <james@kof22.com>
Co-authored-by: Codex (GPT-6) <noreply@openai.com>
2026-10-10 14:56:22 -05:00
james.maesandOpenAI GPT-6 Codex 11fe60b146 fix(releases): allow native formatting in generated changelog (#24)
Scope native spacing, bare-link and repeated-heading exceptions to the generated
changelog. Keep normal Markdown rules for other documentation.

Refs: #21
Validation: all nine required checks passed in CI run 38074899308.
Recorded AI review on PR #24 at source fa15d5cd2a.

Co-Authored-By: OpenAI GPT-6 Codex <noreply@openai.com>
Signed-off-by: James Maes <james@kof22.com>
2026-10-10 13:15:22 -05:00
james.maesandOpenAI GPT-6 Codex 435049d688 fix(releases): use professional repository and automation text (#23)
Configure professional Release Please wording and native release-note categories,
retain complete package notes, and reject emoji in PR titles and descriptions.
Update contributor and agent guidance and remove the decorative IDE symbol.

Fixes: #21
Validation: all nine required PR checks passed in CI run 38074586698.
Recorded AI review: https://github.com/KofTwentyTwo/standards/pull/23#pullrequestreview-5480239702

Co-Authored-By: OpenAI GPT-6 Codex <noreply@openai.com>
Signed-off-by: James Maes <james@kof22.com>
2026-10-10 13:10:10 -05:00
james.maesandOpenAI GPT-6 Codex f7262f97cb feat(assurance)!: require applicable fuzzing and badge participation
Add mandatory parser fuzzing (K22-TEST-25) and public Product Best Practices participation (K22-REPO-12), with scope records, criterion evidence, and adoption templates. The existing required conformance job runs both PowerShell fuzz seeds on PR/main and longer campaigns on weekly schedules and release tags.

Fixes #18. Preserve Scorecard detection limits: native PowerShell property-based fuzzing is running, but is not a claim of a supported integration or a passing badge. EX-0007 covers standards enrollment only, reviewed 2026-10-17 and expiring 2026-11-10 UTC, while the provider blocks maintainer login.

BREAKING CHANGE: Public Product repositories must enroll and maintain an evidence-backed assessment; complex untrusted-input parsers must have required CI fuzz campaigns and documented scope. Publish these new MUSTs in a MAJOR release. Existing immutable v0.1.2 and reserved failed tags remain unchanged; this merge does not publish another release.

Validation: all nine required PR checks passed at aa40477a11, including both native fuzz seeds. Extended local campaigns passed 20000 cases and 320000 assertions. Checker/package regressions, Markdown/links, workflow lint/security, secret scans, configured analysis of new PowerShell code, and full authenticated conformance passed.

AI review: https://github.com/KofTwentyTwo/standards/pull/19#pullrequestreview-5480144854. No blocking code findings remain. Substantial AI assistance: OpenAI GPT-6 Codex, under explicit maintainer implementation and merge authorization. No independent human review or completed badge assessment is claimed.

Signed-off-by: James Maes <james@kof22.com>
Co-Authored-By: OpenAI GPT-6 Codex <noreply@openai.com>
2026-10-10 12:38:20 -05:00
32 changed files with 814 additions and 69 deletions

No files matched your search

+12
View File
@@ -0,0 +1,12 @@
# Plain-language categories for native GitHub release notes.
changelog:
exclude:
labels:
- "autorelease: pending"
- "autorelease: tagged"
- "autorelease: failed"
categories:
- title: Breaking changes
labels: [breaking]
- title: Changes
labels: ["*"]
+10
View File
@@ -93,6 +93,16 @@ jobs:
shell: pwsh
run: ./tools/Test-RepoConformance.ps1 -SelfTest
- name: Conformance parser fuzzing
shell: pwsh
env:
CI_EVENT_NAME: ${{ github.event_name }}
CI_REF_TYPE: ${{ github.ref_type }}
run: |
$iterations = if ($env:CI_EVENT_NAME -eq 'schedule' -or $env:CI_REF_TYPE -eq 'tag') { 10000 } else { 1000 }
./tools/Test-RepoConformance.Fuzz.ps1 -Seed 22023 -Iterations $iterations
./tools/Test-RepoConformance.Fuzz.ps1 -Seed 99173 -Iterations $iterations
- name: Standards package tests
shell: pwsh
run: ./tools/Test-StandardsPackage.ps1
+10
View File
@@ -52,6 +52,7 @@ jobs:
- name: PR title is a Conventional Commit header
env:
PR_TITLE: ${{ github.event.pull_request.title }}
PR_BODY: ${{ github.event.pull_request.body }}
run: |
# The title becomes the squash commit subject on main (K22-REPO-22), so it
# must be a valid Conventional Commits 1.0 header of at most 100 characters.
@@ -65,6 +66,15 @@ jobs:
printf 'Title was: %s\n' "$PR_TITLE"
exit 1
fi
node - <<'NODE'
const emoji = /\p{Emoji_Presentation}|[\u2600-\u27BF]|\p{Extended_Pictographic}\uFE0F|[#*0-9]\uFE0F?\u20E3/u;
for (const [field, text] of [['title', process.env.PR_TITLE], ['description', process.env.PR_BODY || '']]) {
if (emoji.test(text)) {
console.error(`::error title=Unprofessional pull request::The ${field} contains an emoji; use professional text.`);
process.exit(1);
}
}
NODE
echo "PR title OK"
dco:
+65 -10
View File
@@ -336,9 +336,29 @@ jobs:
tenant-id: ${{ vars.AZURE_TENANT_ID }}
subscription-id: ${{ vars.AZURE_SUBSCRIPTION_ID }}
# Signs the app's own executables and DLLs (and the CLI) before packing, so the
# binaries inside the installer, the portable zip, and the update packages are
# all signed (K22-REL-05).
# Signs the executables and DLLs under publish that carry no valid signature
# yet (the app's own binaries and unsigned third-party ones), so the binaries
# inside the installer, the portable zip and the update packages are all
# signed (K22-REL-05). Already-signed files (the .NET runtime, Windows App SDK)
# keep their publisher's signature and are not re-signed: a self-contained
# publish holds ~300 of them at ~2 s each, which is how the first real run
# (gclo v1.0.2-beta.2, #26) hit the signing action's 300 s default timeout.
- name: Select the binaries to sign
if: inputs.sign
id: sign-list
shell: pwsh
env:
K22_MAIN_EXE: ${{ inputs.main-exe }}
K22_CLI_PROJECT: ${{ inputs.cli-project }}
run: |
$all = Get-ChildItem publish -Recurse -Include *.exe, *.dll
$unsigned = @($all | Where-Object { (Get-AuthenticodeSignature -LiteralPath $_.FullName).Status -ne 'Valid' })
$names = $unsigned | ForEach-Object { $_.Name }
if ($names -notcontains $env:K22_MAIN_EXE) { throw "publish/app/$env:K22_MAIN_EXE is missing or already signed by someone else." }
if ($env:K22_CLI_PROJECT -and -not ($unsigned | Where-Object { $_.Extension -eq '.exe' -and $_.FullName -like '*\publish\cli\*' })) { throw 'publish/cli has no unsigned exe to sign.' }
Write-Output "Signing $($unsigned.Count) of $($all.Count) binaries; $($all.Count - $unsigned.Count) already carry a valid signature."
"files<<EOF`n$(($unsigned.FullName) -join "`n")`nEOF" | Add-Content -LiteralPath $env:GITHUB_OUTPUT
- name: Sign published binaries (Azure Trusted Signing)
if: inputs.sign
uses: azure/artifact-signing-action@c7ab2a863ab5f9a846ddb8265964877ef296ee82 # v2.0.0
@@ -346,12 +366,11 @@ jobs:
endpoint: ${{ vars.TRUSTED_SIGNING_ENDPOINT }}
signing-account-name: ${{ vars.TRUSTED_SIGNING_ACCOUNT }}
certificate-profile-name: ${{ vars.TRUSTED_SIGNING_PROFILE }}
files-folder: ${{ github.workspace }}\publish
files-folder-filter: exe,dll
files-folder-recurse: true
files: ${{ steps.sign-list.outputs.files }}
file-digest: SHA256
timestamp-rfc3161: http://timestamp.acs.microsoft.com
timestamp-digest: SHA256
timeout: 1800
cache-dependencies: false
exclude-environment-credential: true
exclude-workload-identity-credential: true
@@ -363,9 +382,13 @@ jobs:
exclude-azure-developer-cli-credential: true
exclude-interactive-browser-credential: true
# vpk signs what it adds (Setup.exe and the update stub) with the same identity,
# reading this metadata file and the Azure CLI login above.
- name: Write signing metadata for vpk
# vpk signs what it adds (Setup.exe, Update.exe, the execution stub) with the
# same identity. It calls this wrapper once per file (--signTemplate) instead
# of its built-in Azure path (--azureTrustedSignFile), which hung for over ten
# minutes on nine files in testing; the wrapper uses the SignTool and the
# Artifact Signing client the signing step above already installed, with the
# Azure CLI login, and skips files that already carry a valid signature.
- name: Write the signing wrapper for vpk
if: inputs.sign
shell: pwsh
env:
@@ -378,6 +401,18 @@ jobs:
CodeSigningAccountName = $env:TRUSTED_SIGNING_ACCOUNT
CertificateProfileName = $env:TRUSTED_SIGNING_PROFILE
} | ConvertTo-Json | Set-Content -LiteralPath "$env:RUNNER_TEMP\trusted-signing.json" -Encoding utf8
$signtool = Get-ChildItem "${env:ProgramFiles(x86)}\Windows Kits\10\bin" -Recurse -Filter signtool.exe |
Where-Object { $_.FullName -match '\\x64\\' } | Sort-Object FullName -Descending | Select-Object -First 1
$dlib = Get-ChildItem "$env:LOCALAPPDATA\ArtifactSigning" -Recurse -Filter Azure.CodeSigning.Dlib.dll |
Where-Object { $_.FullName -match '\\x64\\' } | Sort-Object FullName -Descending | Select-Object -First 1
if (-not $signtool -or -not $dlib) { throw "signtool.exe ($signtool) or Azure.CodeSigning.Dlib.dll ($dlib) not found." }
@"
param([Parameter(Mandatory)][string] `$File)
`$ErrorActionPreference = 'Stop'
if ((Get-AuthenticodeSignature -LiteralPath `$File).Status -eq 'Valid') { exit 0 }
& '$($signtool.FullName)' sign /fd SHA256 /tr http://timestamp.acs.microsoft.com /td SHA256 /dlib '$($dlib.FullName)' /dmdf '$env:RUNNER_TEMP\trusted-signing.json' `$File | Out-Null
if (`$LASTEXITCODE -ne 0) { throw "signtool failed (`$LASTEXITCODE) on `$File" }
"@ | Set-Content -LiteralPath "$env:RUNNER_TEMP\sign-one.ps1" -Encoding utf8
# The vpk version matches the Velopack NuGet package the apps reference (AppKit).
- name: Install vpk
@@ -401,10 +436,30 @@ jobs:
shell: pwsh
run: |
$sign = @()
if ($env:K22_SIGN -eq 'true') { $sign = @('--azureTrustedSignFile', "$env:RUNNER_TEMP\trusted-signing.json") }
if ($env:K22_SIGN -eq 'true') { $sign = @('--signTemplate', "pwsh -NoProfile -NonInteractive -File `"$env:RUNNER_TEMP\sign-one.ps1`" `"{{file}}`"") }
vpk pack --packId $env:K22_APP_ID --packVersion $env:K22_VERSION --packDir publish/app --mainExe $env:K22_MAIN_EXE --channel $env:K22_CHANNEL --icon $env:K22_ICON --outputDir Releases @sign
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
# Nothing unsigned leaves this job: Setup.exe and every executable inside the
# portable zip (the app, Update.exe, the execution stub) must verify before the
# release is even drafted (K22-REL-05).
- name: Verify signatures before packaging further
if: inputs.sign
shell: pwsh
run: |
$signtool = Get-ChildItem "${env:ProgramFiles(x86)}\Windows Kits\10\bin" -Recurse -Filter signtool.exe |
Where-Object { $_.FullName -match '\\x64\\' } | Sort-Object FullName -Descending | Select-Object -First 1
Expand-Archive "Releases/$env:K22_APP_ID-$env:K22_CHANNEL-Portable.zip" -DestinationPath "$env:RUNNER_TEMP\portable" -Force
$files = @(Get-Item "Releases/$env:K22_APP_ID-$env:K22_CHANNEL-Setup.exe") + @(Get-ChildItem "$env:RUNNER_TEMP\portable" -Recurse -Include *.exe, *.dll)
$failed = @()
foreach ($f in $files)
{
& $signtool.FullName verify /pa /all $f.FullName | Out-Null
if ($LASTEXITCODE -ne 0) { $failed += $f.FullName }
}
if ($failed) { Write-Output "::error::Unsigned or invalid after packing:`n$($failed -join "`n")"; exit 1 }
Write-Output "All $($files.Count) packaged executables and libraries verify with signtool."
# Keeps only what this build produced, as `vpk upload` does: the files listed in
# assets.<channel>.json, plus a releases.<channel>.json feed trimmed to this build's
# packages (the local feed may also list the downloaded baseline).
+1 -1
View File
@@ -1 +1 @@
{".":"0.1.2"}
{".":"1.0.0"}
+19 -3
View File
@@ -1,8 +1,24 @@
# Changelog
<!-- Generated by Release Please; permit its section spacing. -->
<!-- markdownlint-disable MD012 -->
<!-- markdownlint-configure-file { "MD024": { "siblings_only": true } } -->
<!-- Generated by Release Please and GitHub; permit native spacing, links and repeated categories. -->
<!-- markdownlint-disable MD012 MD022 MD024 MD032 MD034 -->
## 1.0.0 (2026-10-10)
<!-- Release notes generated using configuration in .github/release.yml at main -->
## What's Changed
### Breaking changes
* feat(assurance)!: require applicable fuzzing and badge participation by @KofTwentyTwo in https://github.com/KofTwentyTwo/standards/pull/19
### Changes
* fix(releases): use professional repository and automation text by @KofTwentyTwo in https://github.com/KofTwentyTwo/standards/pull/23
* fix(releases): allow native formatting in generated changelog by @KofTwentyTwo in https://github.com/KofTwentyTwo/standards/pull/24
* docs(assurance): record OpenSSF passing badge by @KofTwentyTwo in https://github.com/KofTwentyTwo/standards/pull/25
* docs(exceptions): scope temporary frontend audit exception by @KofTwentyTwo in https://github.com/KofTwentyTwo/standards/pull/27
* ci(release-velopack): sign only unsigned files; verify before packing by @KofTwentyTwo in https://github.com/KofTwentyTwo/standards/pull/29
**Full Changelog**: https://github.com/KofTwentyTwo/standards/compare/v0.1.2...v1.0.0
## [0.1.2](https://github.com/KofTwentyTwo/standards/compare/v0.1.1...v0.1.2) (2026-10-10)
+6
View File
@@ -22,6 +22,8 @@ themselves: everything below comes from the [SDLC policy](policies/sdlc.md).
## What a good change looks like
- Use professional wording in code comments, documentation, commit/PR messages,
release notes and automation output. No emojis, joke copy or bot sound effects.
- A new or changed requirement follows the format in
[policies/README.md](policies/README.md#requirement-ids): an ID that has never been used,
a level, the rule, *Why*, and *Verified by*. A requirement nobody can verify is not
@@ -41,6 +43,7 @@ Documentation and package checks:
npx markdownlint-cli2 "**/*.md"
lychee --offline --include-fragments --no-progress './**/*.md'
pwsh tools/Test-RepoConformance.ps1 -SelfTest
pwsh tools/Test-RepoConformance.Fuzz.ps1 -Seed 22023 -Iterations 1000
pwsh tools/Test-StandardsPackage.ps1
pwsh tools/Test-RepoConformance.ps1 -LocalPath . -StaticOnly
```
@@ -59,6 +62,9 @@ pwsh tools/Test-RepoConformance.ps1 -Repository KofTwentyTwo/standards -LocalPat
```
The same checks run in CI on every pull request and must pass before merging.
Parser changes must pass both CI seeds and retain any discovered failure as a
deterministic regression test. See [fuzzing](docs/security/fuzzing.md) for extended
campaigns and [badge assessment](docs/security/best-practices.md) for enrollment evidence.
## License of contributions
+23 -13
View File
@@ -7,23 +7,23 @@ reusable GitHub Actions workflows, repository templates, and a conformance check
[![Docs: CC BY 4.0](https://img.shields.io/badge/docs-CC%20BY%204.0-lightgrey)](LICENSE-docs)
[![Code: MIT](https://img.shields.io/badge/code-MIT-green)](LICENSE)
[![Status: draft](https://img.shields.io/badge/status-draft%20(pre--1.0)-orange)](#status)
[![OpenSSF Best Practices](https://www.bestpractices.dev/projects/15366/badge)](https://www.bestpractices.dev/projects/15366)
[![Status: versioned](https://img.shields.io/badge/status-versioned-blue)](#status)
## Status
> **Draft standards, before `v1.0.0`.** The requirements are written and the
> enforcement tooling exists, but no KofTwentyTwo repository has been verified against
> them yet; adoption starts with this repository, then
> [AppKit](https://github.com/KofTwentyTwo/AppKit) and
> [gclo](https://github.com/KofTwentyTwo/gclo). The compliance crosswalks therefore list
> controls as **Defined**, not met, and the response times in the security program are
> targets until `v1.0.0`. See [how these standards work](policies/README.md).
> **Versioned standards with repository-specific adoption.** Immutable releases
> identify each published baseline. This repository has completed repository and CI
> conformance checks; other repositories record their own checks and exceptions.
> Compliance crosswalks describe requirements and evidence for individual controls.
> See [how these standards work](policies/README.md) and the
> [release checklists](https://github.com/KofTwentyTwo/standards/issues?q=is%3Aissue+%22Release+v%22).
---
**Tier:** Product ([scope](policies/README.md#scope)). **Conformance:** this repository is
the first to adopt the standards; it will state *Conforms to KofTwentyTwo standards
v1.0* once the checker passes on it.
**Tier:** Product ([scope](policies/README.md#scope)). **Conformance:** repository and
CI checks reviewed on 2026-10-10: zero FAIL or UNKNOWN, with manual review items
resolved in the release checklist and approved [exceptions](exceptions/register.md).
## How to use
@@ -33,6 +33,11 @@ v1.0* once the checker passes on it.
the [reusable workflows](#use-the-reusable-workflows), and keep it green with the
[conformance checker](#check-a-repositorys-conformance).
Security assurance is recorded in the [Best Practices assessment](docs/security/best-practices.md)
and [parser fuzzing guide](docs/security/fuzzing.md). The repository earned the
[OpenSSF Best Practices passing badge](https://www.bestpractices.dev/projects/15366)
on 2026-10-10 through a maintainer self-assessment.
## Contents
- [How to use](#how-to-use)
@@ -197,13 +202,18 @@ IDE does on save is what CI checks.
`protect-release-tags` rulesets, squash-only merging, the security features
(private vulnerability reporting, secret scanning with push protection, Dependabot,
code scanning, immutable releases), and read-only Actions tokens.
6. **Check.** Run the conformance checker and fix every FAIL:
6. **Assurance.** Complete `docs/security/assurance.json` and its evidence templates.
Public Product repositories register their own Best Practices assessment and
link the actual badge. Identify complex untrusted-input parsers; add their fuzz
commands to a required PR/main CI job and weekly/release campaigns, or document
why none apply. Review scope decisions; copied placeholders do not conform.
7. **Check.** Run the conformance checker and fix every FAIL:
```powershell
pwsh ./tools/Test-RepoConformance.ps1 -Repository KofTwentyTwo/<repo> -LocalPath ../<repo>
```
7. **Declare it.** Add *"Conforms to KofTwentyTwo standards vX.Y"* to the README and
8. **Declare it.** Add *"Conforms to KofTwentyTwo standards vX.Y"* to the README and
list the repository in [MAINTAINERS.md](MAINTAINERS.md#repositories).
### An existing repository
+3 -2
View File
@@ -38,7 +38,7 @@ this crosswalk is updated when it becomes final.
| **PW.5** Create source code by adhering to secure coding practices | [Coding standards](../standards/coding/README.md), K22-AI-30 |
| **PW.6** Configure build processes to improve executable security | Language profiles (warnings as errors, analyzers, deterministic builds), K22-CI-30 |
| **PW.7** Review and/or analyze human-readable code | K22-SDLC-12, K22-CI-22 |
| **PW.8** Test executable code | [Testing standard](../standards/testing.md) |
| **PW.8** Test executable code | [Testing standard](../standards/testing.md), including K22-TEST-25 parser fuzzing where applicable |
| **PW.9** Configure software to have secure settings by default | K22-SEC-70, K22-SEC-71 |
| **RV.1** Identify and confirm vulnerabilities on an ongoing basis | K22-CI-21, K22-CI-22 (weekly scans), K22-DEP-40, K22-SEC-40 |
| **RV.2** Assess, prioritize, and remediate vulnerabilities | K22-SEC-41, K22-SEC-42 |
@@ -72,4 +72,5 @@ Scorecard checks and the requirement that drives each:
| Vulnerabilities | K22-SEC-41 |
| Binary-Artifacts | K22-REPO-05 |
| Packaging | K22-REL-11 |
| Fuzzing | K22-TEST-23 (where applicable) |
| Fuzzing | K22-TEST-25 (mandatory where applicable); K22-TEST-23 negative-test guidance. Detection supports selected integrations and can miss native harnesses |
| CII-Best-Practices | K22-REPO-12 (public Product enrollment and maintained assessment); actual badge level is assigned by the provider |
+1 -1
View File
@@ -22,7 +22,7 @@ picks them up on open: *Settings → Editor → Code Style* shows **Project** se
## Per IDE (all projects)
1. **Code style:** *Settings → Editor → Code Style → ⚙ (Show Scheme Actions) → Import
1. **Code style:** *Settings → Editor → Code Style → Show Scheme Actions → Import
Scheme → IntelliJ IDEA code style XML* → choose `Kingsrook_Code_Style.xml`.
2. **Reformat on save:** *Settings → Tools → Actions on Save* → enable **Reformat code**
and **Optimize imports**.
+23 -3
View File
@@ -24,10 +24,19 @@ against it before packaging.
Use `feat` for new guidance and `!` for new obligations or removed allowances as
defined in [the versioning policy](../policies/README.md#versioning-of-the-standards).
Infrastructure changes appear in release notes and can produce PATCH releases;
`test`, `style`, and `chore` alone do not produce release notes. Review the proposed
Infrastructure changes can produce PATCH releases;
`test`, `style`, and `chore` alone do not trigger a version proposal. Review the proposed
version and notes before merging. A `breaking` label alone does not change the version.
PR headers and footers are explicitly configured in `release-please-config.json`.
Native GitHub release notes use the plain-language categories in `.github/release.yml`,
keeping breaking changes visible without decorative symbols. Versions still come
from Conventional Commits; note entries link to reviewed PRs. Maintainers review
breaking-change descriptions before publication. Repository text and automation
output use professional wording, without emojis, joke copy or bot sound effects.
Only generated `CHANGELOG.md` permits native spacing, bare links and repeated
category headings; other documentation retains the normal Markdown rules.
## One-time maintainer setup
1. Keep **Settings → General → Releases → Enable release immutability** enabled.
@@ -123,7 +132,7 @@ same Git/archive tool versions. The builder refuses an existing output directory
The initial `v0.1.0` tag was reserved by a workflow-startup failure; no bundle was
published for it. The `v0.1.1` publisher then stopped before draft creation because
the caller omitted the environment secret contract. Both tags remain reserved;
`v0.1.2` is the planned first published bundle. Keep these tags unchanged; use a new
`v0.1.2` is the first published bundle. Keep these tags unchanged; use a new
version when a tagged workflow needs a source fix.
Never replace published assets or move/reuse a version tag. Fix a published error
@@ -136,3 +145,14 @@ from `autorelease: pending` to `autorelease: tagged`; do not rebuild the release
References: [GitHub immutable releases](https://docs.github.com/en/code-security/concepts/supply-chain-security/immutable-releases),
[enabling immutability](https://docs.github.com/en/code-security/how-tos/secure-your-supply-chain/establish-provenance-and-integrity/prevent-release-changes),
and [Release Please token behavior](https://github.com/googleapis/release-please-action#other-actions-on-release-please-prs).
## Known limitations
[Issue #22](https://github.com/KofTwentyTwo/standards/issues/22) tracks Windows App SDK
publish resources and the missing shared launch-smoke gate in `release-velopack.yml`.
Windows consumers must keep `WindowsAppSDKSelfContained` and `WindowsPackageType`
consistent between build and publish, or clean intermediate output before publish.
Run smoke tests against the published executable and test the installer on a clean
machine before a stable application release. This remains a consumer-side check
until the shared workflow implements it. The standards bundle contains source and
configuration; it does not contain a compiled Windows application.
+17
View File
@@ -0,0 +1,17 @@
{
"schemaVersion": 1,
"badge": {
"applicability": "required",
"reason": "Public open-source Product repository distributing standards and executable tooling.",
"projectUrl": "https://www.bestpractices.dev/projects/15366",
"evidence": "docs/security/best-practices.md"
},
"fuzzing": {
"applicability": "required",
"reason": "The conformance checker parses contributor-controlled workflows and exception records; unsafe acceptance can hide failed requirements.",
"targets": ["tools/Test-RepoConformance.ps1"],
"command": "./tools/Test-RepoConformance.Fuzz.ps1",
"workflow": ".github/workflows/ci.yml",
"evidence": "docs/security/fuzzing.md"
}
}
+73
View File
@@ -0,0 +1,73 @@
# OpenSSF Best Practices evidence
## Enrollment and review
**Repository:** <https://github.com/KofTwentyTwo/standards>. **Owner:** @KofTwentyTwo.
**Assessment:** [project 15366](https://www.bestpractices.dev/en/projects/15366/passing).
**Level:** Passing. **Reviewed and earned:** 2026-10-10.
**Next review:** 2027-01-08, or before the next MINOR or MAJOR release, whichever
occurs first. Reassess at least every 90 days.
The public assessment identifies this repository and displays the passing badge.
The maintainer completed the self-assessment, including personal attestations.
This badge records voluntary maintainer self-certification.
The enrollment-only exception [EX-0007](../../exceptions/register.md#ex-0007) is
closed with the actual README badge and project URL recorded in
[assurance.json](assurance.json).
## Criterion evidence matrix
IDs below omit the provider's `0.` level prefix. **Evidence** means source exists;
**Review** marks ongoing operational evidence or human judgment to revalidate;
**Proposed N/A** preserves applicability reasoning for the next review. These are
local evidence notes; the linked public assessment holds the submitted answers.
| Criteria | Evidence and ongoing review | State |
| --- | --- | --- |
| `description_good`, `documentation_basics` | [README](../../README.md) describes standards, tooling and adoption | Evidence |
| `interact`, `discussion`, `report_process`, `report_tracker`, `report_archive` | Public [issues](https://github.com/KofTwentyTwo/standards/issues), templates and [CONTRIBUTING](../../CONTRIBUTING.md) | Evidence |
| `contribution`, `contribution_requirements` | CONTRIBUTING documents proposals, signed commits, DCO, review and gates | Evidence |
| `floss_license`, `floss_license_osi`, `license_location` | Root [MIT](../../LICENSE) for code, configs and templates; [CC BY 4.0](../../LICENSE-docs) for prose; attributed Apache-2.0 material in [NOTICE](../../NOTICE) | Evidence |
| `documentation_interface` | [Tool help and local commands](../../CONTRIBUTING.md#running-the-checks-locally), [release instructions](../releases.md); owner checks shipped tools' help completeness | Review |
| `sites_https`, `delivery_mitm` | GitHub repository and release delivery use HTTPS; [verification](../../standards/releases.md#verifying-a-release) checks provenance | Evidence |
| `english` | Repository documentation and contribution guidance are in English | Evidence |
| `maintained`, `report_responses`, `enhancement_responses` | Recent PRs/issues exist; a new project has limited response history. Assess actual responsiveness, not a policy promise | Review |
| `repo_public`, `repo_track`, `repo_interim`, `repo_distributed` | Public Git repository retains reviewed changes; signed history and protected main | Evidence |
| `version_unique`, `version_semver`, `version_tags`, `release_notes` | Immutable [v0.1.2](https://github.com/KofTwentyTwo/standards/releases/tag/v0.1.2), committed version and [changelog](../../CHANGELOG.md); failed unpublished tags are reserved | Evidence |
| `release_notes_vulns` | [Release standard](../../standards/releases.md) requires security fix notes; no published vulnerability fix history to attest to yet | Review |
| `vulnerability_report_process`, `vulnerability_report_private` | [SECURITY](../../SECURITY.md) links private GitHub reporting and supported versions | Evidence |
| `vulnerability_report_response` | Maintainer confirmed actual response compliance on 2026-10-10 in the public assessment; revalidate report history, not just SECURITY targets | Review |
| `build`, `build_common_tools`, `build_floss_tools` | [Bundle builder](../../tools/Build-StandardsPackage.ps1) uses Git archives and PowerShell; prerequisites and release steps are documented | Evidence |
| `test`, `test_invocation`, `test_policy`, `tests_are_added`, `tests_documented_added` | CONTRIBUTING requires behavioral regression tests and documents checker, package and fuzz commands | Evidence |
| `test_continuous_integration` | [CI](../../.github/workflows/ci.yml) runs tests on PR/main, schedules and tag publication gates | Evidence |
| `test_most` | Parser properties, helper self-tests and package tests exist; measured whole-tool coverage is not established | Suggested: Unmet |
| `warnings`, `warnings_fixed` | Markdown/workflow gates reject findings; revalidate applicable PowerShell warning handling | Review |
| `warnings_strict` | Maximal PowerShell warning/linter coverage across shipped tooling is not established by Actions CodeQL | Suggested: Unmet |
| `know_secure_design`, `know_common_errors` | Primary maintainer personally attested on 2026-10-10; supporting [threat model](threat-model.md), [security policy](../../policies/security-program.md) and parser regressions remain review evidence | Review |
| `crypto_published`, `crypto_call`, `crypto_floss`, `crypto_keylength`, `crypto_working`, `crypto_weaknesses` | No custom product cryptography. GitHub TLS, SSH signing and attestations are delegated mechanisms; assess whether delegation meets each criterion or permits N/A | Review |
| `crypto_pfs` | No application server or product-controlled TLS configuration; document the hosted-service boundary if N/A is permitted | Proposed N/A |
| `crypto_password_storage` | No application passwords, password database or credential-storage implementation | Proposed N/A |
| `crypto_random` | The seeded fuzz PRNG generates only synthetic tests, never security keys/nonces. Cryptographic randomness is delegated to signing/hosting providers | Review |
| `delivery_unsigned` | Release assets have checksums, verified source, build provenance and GitHub immutable-release attestation; see [verification commands](../releases.md) | Evidence |
| `vulnerabilities_fixed_60_days`, `vulnerabilities_critical_fixed` | [Security program](../../policies/security-program.md#vulnerability-management) defines deadlines; maintainer must check current findings and known unpatched issues before answering | Review |
| `no_leaked_credentials` | Required [gitleaks and secret scans](../../.github/workflows/security.yml); scans support, but cannot prove, a universal absence claim | Review |
| `static_analysis`, `static_analysis_common_vulnerabilities`, `static_analysis_often` | [CodeQL Actions](../../.github/workflows/codeql.yml), zizmor and actionlint in required security gates; document PowerShell analysis coverage accurately | Evidence |
| `static_analysis_fixed` | Gates reject applicable findings; review unresolved alerts and [Scorecard dispositions](scorecard-dispositions.md) before claiming all important findings fixed | Review |
| `dynamic_analysis`, `dynamic_analysis_enable_assertions` | Executed [fuzz properties](fuzzing.md), deterministic helper and isolated package tests; assertions fail the job | Evidence |
| `dynamic_analysis_unsafe` | Shipped tooling is managed PowerShell, not unsafe native-language implementation; owner validates applicability | Proposed N/A |
| `dynamic_analysis_fixed` | Generated-input findings gained deterministic regressions; review all future campaign failures before assessment updates | Review |
## Maintaining the assessment
1. Review the public answers against current criteria and repository evidence by the
next review date and before each MINOR or MAJOR release.
2. Reconfirm human knowledge, unresolved findings and actual response history with
the maintainer; update answers when evidence or applicability changes.
3. Keep the provider's live badge, project URL and this review record synchronized.
Downstream repositories need their own assessment and badge.
4. Improve the unmet suggested criteria `test_most` and `warnings_strict`; passing
does not establish whole-tool coverage or maximal warning coverage. Track the
shared release smoke-test gap in [issue #22](https://github.com/KofTwentyTwo/standards/issues/22).
Downstream repositories use the [assessment template](../../templates/repo/docs/security/best-practices.md)
and their own evidence and project ID; EX-0007 does not apply to them.
+50
View File
@@ -0,0 +1,50 @@
# Conformance parser fuzzing
## Scope and properties
K22-TEST-25 applies to shipped tooling even though most repository content is prose.
`tools/Test-RepoConformance.Fuzz.ps1` generates synthetic workflow and exception input
for the pure helpers in `tools/Test-RepoConformance.ps1`. It varies quotes, indentation,
comments, LF/CRLF, scalar indicators, action references and attacker expressions.
Independent construction supplies the expected security decision: only full SHA
pins pass; credential persistence and untrusted expressions are detected; expressions
in environment fields remain outside script bodies; exceptions match their exact
scope and must be open and unexpired. Bounded malformed ASCII inputs also exercise
parser robustness. These properties test unsafe acceptance, not only crashes.
This is native generated-input fuzzing, not coverage-guided instrumentation or a
complete YAML validator. Zizmor remains the authoritative workflow security gate.
Scorecard currently detects selected integrations; it may continue reporting zero
for this PowerShell harness. Do not claim OSS-Fuzz or ClusterFuzzLite enrollment.
## Running and replaying
PowerShell 7.4 or later is the only dependency; no network or profile is used.
```powershell
pwsh -NoProfile -File tools/Test-RepoConformance.Fuzz.ps1 -Seed 22023 -Iterations 1000
pwsh -NoProfile -File tools/Test-RepoConformance.Fuzz.ps1 -Seed 99173 -Iterations 1000
# Weekly and pre-MINOR/MAJOR campaigns: run both seeds at 10000 iterations.
pwsh -NoProfile -File tools/Test-RepoConformance.Fuzz.ps1 -Seed 22023 -Iterations 10000
pwsh -NoProfile -File tools/Test-RepoConformance.Fuzz.ps1 -Seed 99173 -Iterations 10000
```
Failures print synthetic input, property, iteration and seed as JSON. Replay with
that seed and at least that iteration count against the same checkout and PowerShell
runtime. Minimize the input into a named `-SelfTest` regression, then fix the parser
and rerun both seeds. Report suspected vulnerabilities through [SECURITY.md](../../SECURITY.md).
Inputs are bounded to small workflow fragments and fewer than 128 random ASCII
characters; iterations are limited to 100000. Generated scripts are never executed.
The harness does not write files, launch tools, or request repository credentials.
The required `ci / conformance` job runs both seeds on PRs and `main`, and 10000
iterations per seed on weekly schedules and release-tag validation, within its
10-minute timeout. Future failing seeds must be retained alongside regression cases.
## Review record
- **Owner:** @KofTwentyTwo; **reviewed:** 2026-10-10; **next review:** 2027-01-08.
- Seed 22023 exposed quoted-checkout and explicit-indentation block-scalar gaps;
both now have deterministic self-test regressions. This is not a coverage claim.
- Reassess targets when parser boundaries change; generated data alone does not
establish completeness or substitute for ordinary package and checker tests.
+8 -2
View File
@@ -14,8 +14,14 @@ changes or predict future maintenance.
Keep the accepted risk visible; dismissal is not a claim that the missing second
maintainer exists. Do not disable Scorecard or weaken protections. Reopen and
reassess these dispositions when their assumptions change. The LOW best-practices
badge and MEDIUM fuzzing recommendations remain open for follow-up.
reassess these dispositions when their assumptions change. The
[badge assessment](best-practices.md) records the passing badge for
[project 15366](https://www.bestpractices.dev/projects/15366), earned on 2026-10-10;
EX-0007 is closed. Let the next Scorecard scan reassess the badge finding rather
than claiming a new score before it runs. The MEDIUM fuzzing recommendation remains
visible: [native parser fuzzing](fuzzing.md) supplies generated security properties and
regressions. Scorecard may not recognize this PowerShell harness; do not label a
score of zero as proof that tests did not run or claim a supported integration.
No OpenVEX statement is fabricated for these process findings: no vulnerable shipped
component or exploit was identified. Any actual vulnerability present but not
+16 -1
View File
@@ -1,6 +1,6 @@
# Threat model: standards distribution
- **Version reviewed:** v0.1.0 release-bundle implementation
- **Version reviewed:** v1.0.0 standards baseline
- **Last updated:** 2026-10-10
- **Status:** Implementation reviewed; provisioning is verified in the release checklist.
@@ -71,6 +71,21 @@ trust boundaries change.
## Review log
- **2026-10-10, v1.0.0:** Reviewed changes since v0.1.2: mandatory applicable badge
participation and parser fuzzing, completed passing self-assessment, professional
release text, scoped frontend audit exception, and unsigned-file-only Windows
signing with verification before packing. Distribution credentials, channels,
environment restrictions and provenance boundaries remain unchanged. Badge
metadata adds a public provider link; fuzzing executes synthetic local inputs.
Signing continues to use the Windows consumer's existing Azure OIDC boundary.
The most likely release interruption remains a failed gate or unpublished draft;
recovery preserves tags and immutable assets. The highest-impact distribution
threat remains a compromised credential or pinned Action, constrained by the
existing scoped tokens, protected environments and required checks. Windows
publish/resource and launch-smoke limitations remain tracked in
[issue #22](https://github.com/KofTwentyTwo/standards/issues/22); consumer workarounds
and rehearsal expectations are recorded in [release guidance](../releases.md#known-limitations).
- **2026-10-10, v0.1.2:** Reviewed the explicit reusable-workflow secret contract
after the unpublished v0.1.1 publisher failed. Only the named App key is passed;
its value remains restricted to the reviewer-protected tag environment. Nested
+56
View File
@@ -11,6 +11,8 @@ Open and closed exceptions to the KofTwentyTwo standards. The process is in
| [EX-0004](#ex-0004) | Open | K22-CI-30, K22-REL-04 (shared release workflow) | gclo | When the shared reusable release workflow ships, at the latest 2027-04-09 |
| [EX-0005](#ex-0005) | Open | K22-AI-10 (agents via PR only), K22-AI-11 (in part: unconfirmed pushes) | second-brain, nix, Praetor, Jellyfin | Reviewed yearly, next on 2027-10-10 |
| [EX-0006](#ex-0006) | Open | K22-CI-16 (tag-only environment secrets) | standards | 2027-04-10; review 2027-01-10 |
| [EX-0007](#ex-0007) | Closed | K22-REPO-12 (enrollment) | KofTwentyTwo/standards | Closed 2026-10-10 |
| [EX-0008](#ex-0008) | Open | K22-CI-21 (HIGH dependency finding) | Website-Frontend, development-only braces 3.0.3 | 2026-11-10; review 2026-10-17 |
## EX-0001
@@ -169,3 +171,57 @@ Open and closed exceptions to the KofTwentyTwo standards. The process is in
- **Expires:** 2027-04-10, or earlier when version proposals use credential federation.
- **Owner:** the maintainer.
- **Review date:** 2027-01-10.
## EX-0007
**Best Practices enrollment temporarily blocked by provider anti-spam**
- **Requirements:** `K22-REPO-12` enrollment and the actual README badge only.
- **Scope:** `KofTwentyTwo/standards`; no downstream repository is covered.
- **Reason:** on 2026-10-10 the maintainer could not sign in because of the provider's
anti-spam restriction and explicitly deferred enrollment. No project ID existed
when this exception was approved.
- **Risk:** consumers could not inspect a registered provider assessment during the
enrollment deferral.
- **Compensating controls (while open):** publish the [criterion evidence](../docs/security/best-practices.md)
and pending answers; retain existing security/PR gates and parser fuzzing. Do not
claim participation or a passing badge. Retry normal login once the restriction
clears, register the exact repository URL, and close this exception through a PR.
- **Expires:** 2026-11-10 (UTC), or successful enrollment and README badge publication,
whichever occurs first. **Review:** 2026-10-17.
- **Owner:** @KofTwentyTwo. Approval is recorded by the normal exception-register PR;
this entry does not authorize bypassing CI or publishing an unreviewed assessment.
- **Closed:** 2026-10-10. [Project 15366](https://www.bestpractices.dev/projects/15366)
identifies this repository and has earned the passing badge. The actual linked
badge is in [README](../README.md), the project URL is in
[assurance.json](../docs/security/assurance.json), and the
[assessment record](../docs/security/best-practices.md) sets the next review date.
## EX-0008
**Temporary development dependency audit exception for Website-Frontend**
- **Requirements:** `K22-CI-21` blocks HIGH and CRITICAL findings in the resolved
dependency graph.
- **Scope:** `KofTwentyTwo/Website-Frontend` only; development-only `braces` 3.0.3,
[GHSA-vfj7-8cjw-p6xm](https://github.com/advisories/GHSA-vfj7-8cjw-p6xm)
/ CVE-2026-93687 and npm's propagated findings for that same advisory. No other
advisory, runtime dependency or CRITICAL finding is covered.
- **Reason:** the Next ESLint tooling depends on the affected package and no patched
npm release exists as of 2026-10-10. npm suggests an unrelated parent downgrade;
that does not establish a patched braces release.
- **Risk:** a deliberately deeply nested caller-supplied brace pattern can exhaust
the stack and stop a lint/build process. Website requests and article Markdown do
not control the lint glob configuration. The dependency remains vulnerable.
- **Compensating controls:** the complete audit still runs. A separate production
audit must have zero HIGH/CRITICAL findings. The exception checker validates the
advisory ID, package, locked version, development-only package paths and expiry;
rejects other HIGH/CRITICAL findings, malformed reports and audit errors; and
requires reassessment if the registry reports a new braces release. Lint, tests,
build, secret scanning, SAST and Trivy remain enabled. No global scanner exclusion
is permitted. Website-Frontend configuration cites this ID.
- **Expires:** 2026-11-10 at 00:00 UTC, or a patched upstream release, whichever
occurs first. A newer upstream release blocks acceptance pending reassessment.
- **Owner:** James Maes, repository maintainer.
- **Review date:** 2026-10-17. Remove the consumer exception and close this entry once
the affected dependency is replaced or patched.
+7 -11
View File
@@ -1,16 +1,12 @@
# How these standards work
> **Status: draft, pre-v1.0.0.** The requirements are written but have not been
> released. Until the first release (`v1.0.0`):
>
> - the automated checks and conformance tooling are still being built, so no
> requirement is yet *Enforced* or *Verified* (see [compliance](../compliance/README.md));
> - no KofTwentyTwo repository claims conformance; adoption is in progress, starting
> with this repository, [AppKit](https://github.com/KofTwentyTwo/AppKit), and
> [gclo](https://github.com/KofTwentyTwo/gclo);
> - commitments to people outside the project (vulnerability response and fix times in
> the [security program](security-program.md#vulnerability-management)) are targets
> that the maintainer works to, and become binding commitments at `v1.0.0`.
> **Status: versioned baseline.** Published tags identify immutable requirements.
> Each repository adopts a specific version and records its own conformance evidence
> and approved exceptions. [Compliance](../compliance/README.md) labels describe the
> evidence for each mapped control; publication alone does not verify every consumer.
> External vulnerability response and fix deadlines in the
> [security program](security-program.md#vulnerability-management) become binding
> commitments with adoption of the stable `v1.0.0` baseline.
This repository is the single source of truth for how KofTwentyTwo builds, secures,
and ships software. It has three layers, each more specific than the last:
+4 -11
View File
@@ -2,18 +2,11 @@
"$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json",
"release-type": "simple",
"include-component-in-tag": false,
"bump-minor-pre-major": false,
"changelog-type": "github",
"pull-request-header": "Release proposal generated from reviewed changes on main. Review the version, changes, and validation before merging.",
"pull-request-footer": "Generated by [Release Please](https://github.com/googleapis/release-please) using this repository's release configuration.",
"initial-version": "0.1.0",
"changelog-sections": [
{ "type": "security", "section": "Security", "hidden": false },
{ "type": "feat", "section": "Features", "hidden": false },
{ "type": "fix", "section": "Bug Fixes", "hidden": false },
{ "type": "docs", "section": "Documentation", "hidden": false },
{ "type": "perf", "section": "Performance", "hidden": false },
{ "type": "refactor", "section": "Refactoring", "hidden": false },
{ "type": "build", "section": "Build", "hidden": false },
{ "type": "ci", "section": "Automation", "hidden": false },
{ "type": "revert", "section": "Reverts", "hidden": false }
],
"packages": {
".": { "version-file": "version.txt" }
}
+4 -1
View File
@@ -117,7 +117,10 @@ the pull request description discloses it, per
**K22-AGENT-41 (SHOULD)** An agent reports in concise plain language: what changed,
why, what was verified, and what limitations remain. Document length scales with the
task; decoration (emojis, filler praise) is omitted unless asked for.
task. Repository text, including code comments, documentation, commits, PRs, release
notes and generated automation messages, uses professional wording. Omit emojis,
joke copy, filler praise and bot sound effects. Configure third-party automation to
follow the same rule; its default templates are not an exception.
*Why:* the report is the maintainer's primary review input. *Verified by:* review.
## Context economy
+14
View File
@@ -88,6 +88,20 @@ released software, links to the latest release and the verification instructions
[releases](releases.md#verifying-a-release).
*Why:* users can see the security posture before they install. *Verified by:* review.
**K22-REPO-12 (MUST)** Public, open-source Product repositories enroll in the
[OpenSSF Best Practices program](https://www.bestpractices.dev/en), link their actual
project badge from the README, and maintain an evidence-backed assessment in
`docs/security/best-practices.md`. Reassess at least every 90 days and before each
MINOR or MAJOR release. Record applicability, evidence path, and the actual project
URL in `docs/security/assurance.json` using the [starter record](../templates/repo/docs/security/assurance.json).
Private repositories record their scope and reason instead; Internal repositories
follow the tier rules above. Registration obstacles require a dated exception, not
a placeholder badge or a claim of passing. A passing badge is the adoption target;
enrollment alone does not demonstrate that every criterion is met.
*Why:* a public assessment makes security practice gaps visible and accountable.
*Verified by:* conformance checker (record, evidence and badge link); maintainer
review of the provider's project page, repository identity, and criterion answers.
## Branch and tag protection
Protection is configured with **repository rulesets**, never with legacy branch
+26
View File
@@ -80,3 +80,29 @@ release checklist.
applications expose a data-directory override (for example an environment variable)
that tests use.
*Why:* running tests must be safe on any machine. *Verified by:* review.
## Parser fuzzing
**K22-TEST-25 (MUST)** Repositories with complex parsers of untrusted input (including
workflow, configuration, protocol, archive, and document parsers) maintain fuzz
targets for those boundaries. Use coverage-guided fuzzing where supported; otherwise
use generated or mutated inputs with independent security properties. A crash-only
smoke test is insufficient when acceptance of unsafe input is the relevant failure.
Run bounded campaigns in a merge-gating CI job on every PR and push to `main`, and
longer campaigns at least weekly and before each MINOR or MAJOR release. Document
targets, input limits, invariants, a local replay command, seeds or corpus, CI job,
and failure handling in `docs/security/fuzzing.md`; declare the evidence, targets,
command and workflow in `docs/security/assurance.json`. Preserve minimized failures
as regression tests and fix or triage them before merging. Never execute generated
attacker scripts, use real secrets, or write to a user's profile.
Repositories without a complex untrusted-input parser document that decision and
its rationale in the same files; review applicability when input boundaries change
and at least every 90 days. Documentation-only status does not exempt shipped tooling.
*Why:* malformed inputs can cause unsafe acceptance as well as crashes; repeatable
fuzzing exercises combinations ordinary examples miss. *Verified by:* conformance
checker (scope record, target paths, command and CI reference); required CI execution
and review of properties, corpus and applicability. See the
[native PowerShell example](../docs/security/fuzzing.md). Scorecard recognition is
useful evidence, but is not a substitute for running appropriate targets.
+3
View File
@@ -1,5 +1,8 @@
# Contributing to {{PROJECT_NAME}}
Use professional wording in code comments, documentation, commit/PR messages,
release notes and automation output. No emojis, joke copy or bot sound effects.
Thanks for helping. This project follows the
[KofTwentyTwo standards](https://github.com/KofTwentyTwo/standards); this page is the
short version of what they ask of a contribution.
+5
View File
@@ -51,6 +51,11 @@ sha256sum --check SHA256SUMS
## Security
Report vulnerabilities privately; see [SECURITY.md](SECURITY.md).
Maintain the [Best Practices assessment](docs/security/best-practices.md) and
[parser fuzzing assessment](docs/security/fuzzing.md), with their actual scope and
configuration in [assurance.json](docs/security/assurance.json). After enrollment,
insert the provider's actual linked badge here; placeholders do not demonstrate
participation.
## Contributing
@@ -0,0 +1,17 @@
{
"schemaVersion": 1,
"badge": {
"applicability": "required",
"reason": "Replace with this repository's public Product scope, or a reviewed private/Internal rationale.",
"projectUrl": null,
"evidence": "docs/security/best-practices.md"
},
"fuzzing": {
"applicability": "required",
"reason": "Replace with actual untrusted-input boundaries, or a reviewed not-applicable rationale.",
"targets": [],
"command": "",
"workflow": "",
"evidence": "docs/security/fuzzing.md"
}
}
@@ -0,0 +1,23 @@
# OpenSSF Best Practices assessment
## Project and ownership
For a public open-source Product repository, register its exact canonical URL at
<https://www.bestpractices.dev/en>. Replace `badge.projectUrl` in `assurance.json`
with the actual `https://www.bestpractices.dev/projects/<numeric-id>` URL and add the
provider's real linked badge to the README. Record the owner, review date and next
review within 90 days. Private/Internal scope decisions need a documented rationale.
## Criterion evidence
Use the current [passing criteria](https://www.bestpractices.dev/en/criteria/0).
For each answer, link actual source, policy, test or observed operational evidence.
Separate implemented practice from intentions, pending human attestations, and
justified not-applicable answers. Passing is the target, not an automatic claim
made by a copied template. Record remaining gaps with owners and follow-up dates.
## Maintenance
Reassess at least every 90 days and before each MINOR or MAJOR release. Keep the
provider's answers consistent with this evidence. A registration restriction needs
a dated approved exception; never use another repository's badge or invent an ID.
+29
View File
@@ -0,0 +1,29 @@
# Parser fuzzing assessment
## Applicability
Record the reviewed complex untrusted-input boundaries and their owner. If none
exist, explain why shipped tooling, importers, configuration and protocol handling
do not introduce one. Set `fuzzing.applicability` in `assurance.json` to `required`
or `not-applicable` with the actual rationale. Unfilled templates do not conform.
## Targets and properties
List each target, synthetic input limits, security invariants and corpus or seeds.
Use the language's native coverage-guided fuzzer where practical. Generated-input
property tests are an alternative when instrumentation is unavailable; assertions
must detect unsafe acceptance as well as crashes.
## Run and replay
Provide exact local commands, prerequisites, bounded PR/main campaign budgets,
weekly and pre-MINOR/MAJOR budgets, and the required CI job. Reference the target
paths, command and workflow in `assurance.json`; ensure the command actually runs
there rather than merely appearing in a comment. Explain how to replay and minimize
failures into regression tests. Never execute generated attacker scripts.
## Review
Name the owner, review date and next review (within 90 days). Reassess input
boundaries when they change. Do not claim a Scorecard-supported integration unless
it is genuinely configured and running; a score alone is not a test result.
+3 -1
View File
@@ -44,7 +44,9 @@ if($committedVersion -ne $Version -or $releaseManifest['.'] -ne $Version)
$changelog = (git -C $repository show "${commit}:CHANGELOG.md") -join "`n"
$escapedVersion = [regex]::Escape($Version)
$section = [regex]::Match($changelog, "(?ms)^## (?:\[$escapedVersion\](?:\([^\r\n]*\))?|$escapedVersion)(?=\s|$)[^\r\n]*\r?\n.*?(?=^## |\z)")
# Native release-note categories also use H2; only a version heading ends a release.
$releaseHeading = '^## (?:\[\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?\](?:\([^\r\n]*\))?|\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?)(?=\s|$)'
$section = [regex]::Match($changelog, "(?ms)^## (?:\[$escapedVersion\](?:\([^\r\n]*\))?|$escapedVersion)(?=\s|$)[^\r\n]*\r?\n.*?(?=$releaseHeading|\z)")
if(-not $section.Success)
{
throw "CHANGELOG.md has no release notes for $Version at $commit."
+135
View File
@@ -0,0 +1,135 @@
#Requires -Version 7.4
<#
.SYNOPSIS
Runs bounded, reproducible property tests against the conformance parsers.
.DESCRIPTION
Constructs workflows with independently known security properties, varying quotes,
indentation, comments, line endings, block scalars, and attacker expressions. Inputs
are data only: no generated script is executed and no GitHub access is needed.
A failure prints the seed, iteration, property, and synthetic input for replay.
.PARAMETER Seed
Seed for a deterministic sequence of generated cases.
.PARAMETER Iterations
Number of generated cases; bounded for pull-request CI.
#>
[CmdletBinding()]
param(
[int] $Seed = 22023,
[ValidateRange(1, 100000)]
[int] $Iterations = 1000
)
Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'
. (Join-Path $PSScriptRoot 'Test-RepoConformance.ps1') -LoadHelpers
$random = [System.Random]::new($Seed)
$assertions = 0
$caseNumber = 0
<#
.SYNOPSIS
Fail with replay data when an independently generated security property is violated.
#>
function Assert-Property
{
param([bool] $Condition, [string] $Property, [string] $InputText)
if(-not $Condition)
{
$replay = [ordered]@{ seed = $Seed; iteration = $caseNumber; property = $Property; input = $InputText }
throw ($replay | ConvertTo-Json -Compress)
}
$script:assertions++
}
<#
.SYNOPSIS
Generate synthetic hexadecimal action refs using the injected deterministic PRNG.
#>
function Get-HexString
{
param([int] $Length)
$characters = for($position = 0; $position -lt $Length; $position++)
{
'0123456789abcdef'[$random.Next(16)]
}
return -join $characters
}
for($caseNumber = 1; $caseNumber -le $Iterations; $caseNumber++)
{
$newline = @("`n", "`r`n")[$random.Next(2)]
$indent = ' ' * $random.Next(2, 5)
$quote = @('', "'", '"')[$random.Next(3)]
$sha = Get-HexString 40
$uses = 'actions/checkout@' + $sha
$pinnedWorkflow = @(
'name: generated'
'on: pull_request'
'permissions: {}'
'jobs:'
($indent + 'check:')
($indent * 2 + 'runs-on: ubuntu-24.04')
($indent * 2 + 'steps:')
($indent * 3 + '- uses: ' + $quote + $uses + $quote + ' # pinned')
($indent * 3 + ' with:')
($indent * 3 + ' persist-credentials: false')
) -join $newline
$parsedUses = @(Get-WorkflowUses $pinnedWorkflow)
Assert-Property -Condition ($parsedUses.Count -eq 1) -Property 'one generated action is found' -InputText $pinnedWorkflow
Assert-Property -Condition ((Get-UsesPinState $parsedUses[0].Uses) -eq 'PINNED') -Property 'quoted full SHA remains pinned' -InputText $pinnedWorkflow
Assert-Property -Condition (@(Find-PersistedCheckout $pinnedWorkflow).Count -eq 0) -Property 'explicit credential disabling is found' -InputText $pinnedWorkflow
$unsafeCheckout = $pinnedWorkflow.Replace('persist-credentials: false', 'persist-credentials: true')
Assert-Property -Condition (@(Find-PersistedCheckout $unsafeCheckout).Count -eq 1) -Property 'quoted unsafe checkout is never missed' -InputText $unsafeCheckout
$badRef = @('v7', 'main', (Get-HexString 39), (Get-HexString 41))[$random.Next(4)]
Assert-Property -Condition ((Get-UsesPinState ('actions/checkout@' + $badRef)) -ne 'PINNED') -Property 'non-SHA refs never pass pinning' -InputText $badRef
$expression = @('github.event.pull_request.title', 'github.head_ref', 'inputs.payload')[$random.Next(3)]
$indicator = @('|', '|-', '|+', '>', '>-', '>+', '|2', '|2-', '>2+')[$random.Next(9)]
$expressionText = '${{ ' + $expression + ' }}'
$injectionWorkflow = @(
'permissions: {}'
'jobs:'
($indent + 'check:')
($indent * 2 + 'steps:')
($indent * 3 + '- run: ' + $indicator + ' # generated scalar')
($indent * 3 + ' echo "' + $expressionText + '"')
) -join $newline
$findings = @(Find-TemplateInjection $injectionWorkflow -IncludeInputs)
Assert-Property -Condition ($findings.Count -eq 1) -Property 'untrusted expression in a block scalar is found' -InputText $injectionWorkflow
Assert-Property -Condition ($findings[0].Expression -eq $expression) -Property 'finding preserves the controlled expression' -InputText $injectionWorkflow
$safeWorkflow = $injectionWorkflow.Replace($expressionText, '${{ github.sha }}')
Assert-Property -Condition (@(Find-TemplateInjection $safeWorkflow -IncludeInputs).Count -eq 0) -Property 'trusted expression is not reported' -InputText $safeWorkflow
$envWorkflow = @(
'jobs:'
($indent + 'check:')
($indent * 2 + 'steps:')
($indent * 3 + '- env:')
($indent * 3 + ' TITLE: ' + $expressionText)
($indent * 3 + ' run: echo "$TITLE"')
) -join $newline
Assert-Property -Condition (@(Find-TemplateInjection $envWorkflow -IncludeInputs).Count -eq 0) -Property 'expressions in env stay outside run bodies' -InputText $envWorkflow
$shortName = 'generated-' + $random.Next(100000).ToString([Globalization.CultureInfo]::InvariantCulture)
$requirement = 'K22-CI-' + $random.Next(10, 25).ToString('00', [Globalization.CultureInfo]::InvariantCulture)
$exception = '| [EX-0099](#ex-0099) | Open | ' + $requirement + ' | KofTwentyTwo/' + $shortName + ' | 2099-01-01 |'
$register = @(Read-ExceptionRegister $exception)
Assert-Property -Condition ($register.Count -eq 1) -Property 'generated exception row is parsed' -InputText $exception
Assert-Property -Condition ($null -ne (Find-Exception $register $requirement ('KofTwentyTwo/' + $shortName))) -Property 'exact exception scope matches' -InputText $exception
Assert-Property -Condition ($null -eq (Find-Exception $register $requirement ('KofTwentyTwo/' + $shortName + '-other'))) -Property 'partial repository names cannot gain an exception' -InputText $exception
Assert-Property -Condition ($null -eq (Find-Exception $register $requirement ('AnotherOwner/' + $shortName))) -Property 'explicit scope cannot authorize another owner' -InputText $exception
$closed = @(Read-ExceptionRegister $exception.Replace('| Open |', '| Closed |'))
Assert-Property -Condition ($null -eq (Find-Exception $closed $requirement ('KofTwentyTwo/' + $shortName))) -Property 'closed exceptions cannot authorize a failure' -InputText $exception
$expired = @(Read-ExceptionRegister $exception.Replace('2099-01-01', '2026-11-10'))
Assert-Property -Condition ($null -eq (Find-Exception $expired $requirement ('KofTwentyTwo/' + $shortName) -Today ([datetime] '2026-11-10'))) -Property 'expired exceptions cannot authorize a failure' -InputText $exception
$noise = -join (1..$random.Next(1, 128) | ForEach-Object { [char] $random.Next(32, 127) })
foreach($parser in 'Get-WorkflowUses', 'Get-TopLevelPermissions', 'Get-RunBlocks', 'Find-TemplateInjection', 'Find-PersistedCheckout', 'Read-ExceptionRegister')
{
& $parser $noise | Out-Null
}
Get-AssuranceCheck $noise @() @{} '' | Out-Null
$assertions++
}
Write-Output "PASS: conformance fuzz seed=$Seed iterations=$Iterations assertions=$assertions"
+133 -8
View File
@@ -4,8 +4,9 @@
Checks a repository against the KofTwentyTwo repository and CI/CD standards.
.DESCRIPTION
Evaluates every automatable requirement in standards/repository.md (K22-REPO-*)
and standards/ci-cd.md (K22-CI-*) and prints one row per requirement:
Evaluates automatable requirements in standards/repository.md (K22-REPO-*),
standards/ci-cd.md (K22-CI-*), and the K22-TEST-25 fuzzing scope record, printing
one row per requirement:
PASS the requirement is met
FAIL a MUST is not met (or a SHOULD, reported as WARN instead)
@@ -45,6 +46,9 @@
.PARAMETER SelfTest
Run the checker's own unit tests and exit.
.PARAMETER LoadHelpers
Load pure parser helpers for the fuzz harness without checking a repository.
.EXAMPLE
./tools/Test-RepoConformance.ps1 -Repository KofTwentyTwo/AppKit -LocalPath ../AppKit
@@ -58,7 +62,8 @@ param(
[switch] $StaticOnly,
[string] $StandardsPath = (Split-Path -Parent $PSScriptRoot),
[switch] $Json,
[switch] $SelfTest
[switch] $SelfTest,
[switch] $LoadHelpers
)
Set-StrictMode -Version Latest
@@ -72,7 +77,8 @@ $script:Levels = [ordered]@{
'K22-REPO-01' = 'MUST'; 'K22-REPO-02' = 'MUST'; 'K22-REPO-03' = 'MUST'
'K22-REPO-04' = 'MUST'; 'K22-REPO-05' = 'MUST'; 'K22-REPO-06' = 'MUST'
'K22-REPO-07' = 'MUST'; 'K22-REPO-08' = 'MUST'; 'K22-REPO-09' = 'MUST'
'K22-REPO-10' = 'SHOULD'; 'K22-REPO-11' = 'SHOULD'; 'K22-REPO-20' = 'MUST'
'K22-REPO-10' = 'SHOULD'; 'K22-REPO-11' = 'SHOULD'; 'K22-REPO-12' = 'MUST'
'K22-TEST-25' = 'MUST'; 'K22-REPO-20' = 'MUST'
'K22-REPO-21' = 'MUST'; 'K22-REPO-22' = 'MUST'; 'K22-REPO-30' = 'MUST'
'K22-REPO-31' = 'MUST'; 'K22-REPO-32' = 'SHOULD'; 'K22-REPO-40' = 'MUST'
'K22-REPO-41' = 'SHOULD'; 'K22-REPO-42' = 'MUST'
@@ -169,7 +175,7 @@ function Get-RunBlocks
{
$indent = $Matches[1].Length
$rest = $Matches[2]
if ($rest -and $rest -notmatch '^[|>][+-]?\s*$')
if ($rest -and $rest -notmatch '^[|>](?:[1-9][+-]?|[+-][1-9]?|[+-]?)\s*(?:#.*)?$')
{
[pscustomobject]@{ Line = $i + 1; Body = $rest }
continue
@@ -226,7 +232,7 @@ function Find-PersistedCheckout
$lines = $Text -split "`r?`n"
for ($i = 0; $i -lt $lines.Count; $i++)
{
if ($lines[$i] -match '^(\s*)(-\s+)?uses:\s*actions/checkout@')
if ($lines[$i] -match '^(\s*)(-\s+)?uses:\s*["'']?actions/checkout@')
{
$stepIndent = $Matches[1].Length
$ok = $false
@@ -263,6 +269,7 @@ function Read-ExceptionRegister
Requirements = @($qualifiers.Keys)
Qualifiers = $qualifiers
Scope = $cells[3]
Expires = if ($cells.Count -ge 5) { $cells[4] } else { '' }
}
}
}
@@ -270,10 +277,18 @@ function Read-ExceptionRegister
# Returns the open exception covering a requirement for a repository, or $null.
function Find-Exception
{
param([object[]] $Register, [string] $RequirementId, [string] $RepositoryName, [string[]] $Aspects = @())
param([object[]] $Register, [string] $RequirementId, [string] $RepositoryName, [string[]] $Aspects = @(), [datetime] $Today = [datetime]::UtcNow.Date)
foreach ($ex in $Register)
{
if ($ex.Status -notmatch '^Open$') { continue }
# Review dates are not expiry dates. Enforce an explicit ISO expiry or an
# "at the latest" deadline; condition-only expiry still needs human review.
if ($ex.Expires -match '(?i)(?:^|at (?:the )?latest\s+)(\d{4}-\d{2}-\d{2})')
{
$deadline = [datetime]::MinValue
if (-not [datetime]::TryParseExact($Matches[1], 'yyyy-MM-dd', [Globalization.CultureInfo]::InvariantCulture, [Globalization.DateTimeStyles]::None, [ref] $deadline)) { continue }
if ($Today.Date -ge $deadline.Date) { continue }
}
if ($ex.Requirements -notcontains $RequirementId) { continue }
# A qualified exception covers a failure only when every failing aspect is
# the qualified one.
@@ -285,7 +300,8 @@ function Find-Exception
}
$short = if ($RepositoryName) { ($RepositoryName -split '/')[-1] } else { '' }
if ($ex.Scope -match '(?i)^all repositories' -or
($short -and $ex.Scope -match "(?i)(^|[\s/,(])$([regex]::Escape($short))($|[\s,)])"))
($RepositoryName -and $ex.Scope -match "(?i)(^|[\s,(])$([regex]::Escape($RepositoryName))($|[\s,)])") -or
($short -and $ex.Scope -match "(?i)(^|[\s,(])$([regex]::Escape($short))($|[\s,)])"))
{
return $ex
}
@@ -293,6 +309,78 @@ function Find-Exception
return $null
}
<#
.SYNOPSIS
Validate declared assurance evidence without executing commands or claiming certification.
#>
function Get-AssuranceCheck
{
param([string] $Text, [string[]] $Paths, [hashtable] $Workflows, [string] $Readme)
try
{
$record = ConvertFrom-Json -InputObject $Text -AsHashtable -ErrorAction Stop
if ($record -isnot [System.Collections.IDictionary] -or $record['schemaVersion'] -isnot [long] -or $record['schemaVersion'] -ne 1) { throw 'unsupported schema' }
}
catch
{
foreach ($id in 'K22-REPO-12', 'K22-TEST-25')
{
[pscustomobject]@{ Id = $id; Status = 'FAIL'; Detail = 'missing or invalid docs/security/assurance.json (schemaVersion 1)'; Aspects = @('record') }
}
return
}
foreach ($pair in @(@('badge', 'K22-REPO-12'), @('fuzzing', 'K22-TEST-25')))
{
$kind, $id = $pair
$entry = $record[$kind]
$status, $detail, $aspect = 'FAIL', 'invalid assurance scope, rationale or evidence path', 'record'
if ($entry -is [System.Collections.IDictionary] -and
$entry['applicability'] -in @('required', 'not-applicable') -and
$entry['reason'] -is [string] -and -not [string]::IsNullOrWhiteSpace($entry['reason']) -and
$entry['evidence'] -is [string] -and $entry['evidence'] -cmatch '^docs/security/[A-Za-z0-9._/-]+\.md$' -and
$entry['evidence'] -notmatch '(^|/)\.\.?(/|$)' -and $Paths -ccontains $entry['evidence'])
{
if ($entry['applicability'] -eq 'not-applicable')
{
$status, $detail = 'REVIEW', "$kind scope declared not applicable; verify rationale and input/public-Product scope in $($entry.evidence)"
}
elseif ($kind -eq 'badge')
{
$aspect = 'enrollment'
$detail = 'badge enrollment missing; register this repository or obtain a dated enrollment exception'
if ($entry['projectUrl'] -is [string] -and $entry['projectUrl'] -cmatch '^https://www\.bestpractices\.dev/(?:en/)?projects/([1-9][0-9]*)/?$')
{
$projectId = $Matches[1]
$badgePattern = '\[!\[[^\]]*\]\(https://www\.bestpractices\.dev/projects/' + $projectId + '/badge\)\]\(https://www\.bestpractices\.dev/(?:en/)?projects/' + $projectId + '/?\)'
if ($Readme -cmatch $badgePattern)
{
$status, $detail = 'REVIEW', "badge linked; verify $($entry.projectUrl) belongs to this repository, current answers and 90-day review in $($entry.evidence)"
}
else { $detail = 'README lacks the actual linked badge matching the declared project ID' }
}
}
else
{
$aspect = 'fuzzing'
$targets = @($entry['targets'])
$validTargets = $targets.Count -gt 0 -and @($targets | Where-Object {
$_ -isnot [string] -or $_ -match '(^[/\\]|^[A-Za-z]:|(^|[/\\])\.\.?([/\\]|$))' -or $Paths -cnotcontains $_
}).Count -eq 0
$validWorkflow = $entry['workflow'] -is [string] -and $entry['workflow'] -cmatch '^\.github/workflows/[^/]+\.ya?ml$' -and $Workflows.ContainsKey($entry['workflow'])
$validCommand = $entry['command'] -is [string] -and -not [string]::IsNullOrWhiteSpace($entry['command']) -and $entry['command'] -notmatch '[\r\n]'
$runs = if ($validWorkflow) { @(Get-RunBlocks $Workflows[$entry['workflow']] | ForEach-Object Body) -join "`n" } else { '' }
$commandInRun = $validCommand -and $runs -cmatch ('(?m)^\s*' + [regex]::Escape($entry['command'].Trim()) + '(?:\s|$)')
if ($validTargets -and $validWorkflow -and $commandInRun)
{
$status, $detail = 'REVIEW', "fuzz targets and run command found; verify required PR/main gate, weekly/release budgets, properties and replay in $($entry.evidence)"
}
else { $detail = 'declared fuzz targets must exist and the command must begin a run line in the declared workflow' }
}
}
[pscustomobject]@{ Id = $id; Status = $status; Detail = $detail; Aspects = @($aspect) }
}
}
# Lock files expected for each manifest found (K22-REPO-09).
$script:LockRules = @(
@{ Manifest = '(^|/)[^/]+\.(cs|fs|vb)proj$|(^|/)Directory\.Packages\.props$'; Locks = 'packages\.lock\.json$'; Name = 'NuGet (packages.lock.json)' }
@@ -395,6 +483,8 @@ jobs:
Assert ($inj[1].Expression -eq 'github.head_ref') 'injection: head_ref'
$persisted = @(Find-PersistedCheckout $wf)
Assert ($persisted.Count -eq 1 -and $persisted[0] -eq 9) "checkout: persisted ($($persisted -join ','))"
Assert (@(Find-PersistedCheckout "jobs:`n check:`n steps:`n - uses: 'actions/checkout@v7'`n with:`n persist-credentials: true").Count -eq 1) 'checkout: quoted unsafe action'
Assert (@(Find-TemplateInjection "jobs:`n check:`n steps:`n - run: |2 # explicit indentation`n echo `${{ github.head_ref }}").Count -eq 1) 'injection: block scalar indentation and comment'
$register = @"
| ID | Status | Requirements | Scope | Expires |
@@ -416,6 +506,24 @@ jobs:
Assert ($null -eq (Find-Exception $parsed 'K22-REPO-05' 'KofTwentyTwo/AppKit')) 'exception: closed'
Assert ((Find-Exception $parsed 'K22-CODE-CS-03' 'KofTwentyTwo/AppKit').Id -eq 'EX-0004') 'exception: short name scope'
Assert ($null -eq (Find-Exception $parsed 'K22-CODE-CS-03' 'KofTwentyTwo/AppKitExtras')) 'exception: no partial name match'
$expired = @(Read-ExceptionRegister '| [EX-0099](#ex-0099) | Open | K22-REPO-12 | standards | 2026-11-10; review 2026-10-17 |')
Assert ($null -ne (Find-Exception $expired 'K22-REPO-12' 'standards' -Today ([datetime] '2026-11-09'))) 'exception: valid before deadline'
Assert ($null -eq (Find-Exception $expired 'K22-REPO-12' 'standards' -Today ([datetime] '2026-11-10'))) 'exception: expiry cannot silently authorize failure'
$exactScope = @(Read-ExceptionRegister '| [EX-0099](#ex-0099) | Open | K22-REPO-12 | KofTwentyTwo/standards | 2099-01-01 |')
Assert ($null -eq (Find-Exception $exactScope 'K22-REPO-12' 'AnotherOwner/standards')) 'exception: explicit owner scope cannot match another owner'
Assert (@(Get-AssuranceCheck '{}' @() @{} '').Count -eq 2) 'assurance: missing schema returns both failures'
Assert (@(Get-AssuranceCheck '{"schemaVersion":true}' @() @{} '' | Where-Object Status -eq 'FAIL').Count -eq 2) 'assurance: boolean cannot masquerade as schema version 1'
Assert (@(Get-AssuranceCheck '{"schemaVersion":1,"badge":{},"fuzzing":{}}' @() @{} '' | Where-Object Status -eq 'FAIL').Count -eq 2) 'assurance: incomplete entries fail without crashing'
$assurance = '{"schemaVersion":1,"badge":{"applicability":"required","reason":"public Product","projectUrl":"https://www.bestpractices.dev/projects/123","evidence":"docs/security/best-practices.md"},"fuzzing":{"applicability":"required","reason":"parser","targets":["tools/parser.ps1"],"command":"./tools/fuzz.ps1","workflow":".github/workflows/ci.yml","evidence":"docs/security/fuzzing.md"}}'
$assurancePaths = @('tools/parser.ps1', 'docs/security/best-practices.md', 'docs/security/fuzzing.md')
$assuranceWorkflow = @{ '.github/workflows/ci.yml' = "jobs:`n tests:`n steps:`n - run: ./tools/fuzz.ps1 -Iterations 10" }
$assuranceReadme = '[![Best Practices](https://www.bestpractices.dev/projects/123/badge)](https://www.bestpractices.dev/projects/123)'
$assuranceRows = @(Get-AssuranceCheck $assurance $assurancePaths $assuranceWorkflow $assuranceReadme)
Assert (@($assuranceRows | Where-Object Status -ne 'REVIEW').Count -eq 0) 'assurance: evidence needs human verification, never fabricated certification'
Assert (@(Get-AssuranceCheck $assurance $assurancePaths $assuranceWorkflow '' | Where-Object { $_.Id -eq 'K22-REPO-12' -and $_.Status -eq 'FAIL' }).Count -eq 1) 'assurance: missing badge fails'
$assuranceWorkflow['.github/workflows/ci.yml'] = "jobs:`n tests:`n steps:`n - run: |`n # ./tools/fuzz.ps1"
Assert (@(Get-AssuranceCheck $assurance $assurancePaths $assuranceWorkflow $assuranceReadme | Where-Object { $_.Id -eq 'K22-TEST-25' -and $_.Status -eq 'FAIL' }).Count -eq 1) 'assurance: comment does not execute a fuzz command'
Assert (@(Get-AssuranceCheck ($assurance.Replace('tools/parser.ps1', '../parser.ps1')) $assurancePaths $assuranceWorkflow $assuranceReadme | Where-Object { $_.Id -eq 'K22-TEST-25' -and $_.Status -eq 'FAIL' }).Count -eq 1) 'assurance: traversal paths cannot satisfy targets'
Assert (@(Get-MissingLocks @('src/a/a.csproj', 'README.md')) -contains 'NuGet (packages.lock.json)') 'locks: missing nuget'
Assert (@(Get-MissingLocks @('src/a/a.csproj', 'src/a/packages.lock.json')).Count -eq 0) 'locks: nuget ok'
@@ -450,6 +558,7 @@ jobs:
exit 0
}
if ($LoadHelpers) { return }
if ($SelfTest) { Invoke-SelfTest }
# =================================================================================
@@ -534,6 +643,13 @@ function Find-FirstPath
}
$repoName = if ($Repository) { $Repository } else { Split-Path -Leaf $script:LocalRoot }
if (-not $Repository)
{
# Local static checks can identify exact exception scope without contacting
# GitHub. A clone with no canonical GitHub origin keeps its directory name.
$origin = & git -C $script:LocalRoot remote get-url origin 2>$null
if ($LASTEXITCODE -eq 0 -and $origin -match '^(?:https://github\.com/|git@github\.com:|ssh://git@(?:github\.com(?::22)?|ssh\.github\.com:443)/)([^/]+/[^/]+?)(?:\.git)?$') { $repoName = $Matches[1] }
}
# =================================================================================
# Static checks: required files (K22-REPO-01 .. 11)
@@ -662,6 +778,15 @@ $workflows = @{}
foreach ($p in $workflowPaths) { $workflows[$p] = Get-FileText $p }
$allWorkflowText = ($workflows.Values -join "`n")
# Assurance records fail closed when required evidence is absent. A positive static
# check is REVIEW: identity, applicability and actual CI execution need verification.
$assuranceReadme = if ($readmePath) { Get-FileText $readmePath } else { '' }
if ($assuranceReadme -match '(?im)\bTier(?:\*\*)?\s*[:|]\s*(?:\*\*)?\s*Internal\b') { $script:Levels['K22-REPO-12'] = 'SHOULD' }
foreach ($check in Get-AssuranceCheck (Get-FileText 'docs/security/assurance.json') $script:Paths $workflows $assuranceReadme)
{
Add-Result $check.Id $check.Status $check.Detail $check.Aspects
}
function Test-Calls
{
# True when any workflow calls the named standards reusable workflow (by remote
+17
View File
@@ -63,6 +63,14 @@ Future notes.
Current notes.
## Changes
Native category notes.
## Breaking changes
Current breaking details.
## 1.2.2
Older notes.
@@ -100,6 +108,7 @@ $manifest = Get-Content -LiteralPath (Join-Path $first "$prefix.manifest.json")
Test-Condition ($manifest.version -eq '1.2.3' -and $manifest.commit -eq (git -C $source rev-parse HEAD)) 'manifest identifies version and exact source commit'
$notes = Get-Content -LiteralPath (Join-Path $first "$prefix.notes.md") -Raw
Test-Condition ($notes -match 'Current notes' -and $notes -notmatch 'Older notes|Future notes' -and $notes.Contains($checklist)) 'notes contain only this release and its checklist'
Test-Condition ($notes -match 'Native category notes' -and $notes -match 'Current breaking details') 'notes preserve native H2 categories within a release'
foreach($file in (Get-ChildItem -LiteralPath $first -File))
{
Test-Condition ((Get-FileHash -LiteralPath $file.FullName).Hash -eq (Get-FileHash -LiteralPath (Join-Path $second $file.Name)).Hash) "$($file.Name) is reproducible"
@@ -115,6 +124,14 @@ Test-Exception { & $builder -Version '1.2.4' -RepositoryPath $source -OutputPath
Test-Exception { & $builder -Version '01.2.3' -RepositoryPath $source } 'ValidatePattern|validation|pattern'
Test-Exception { & $builder -Version '1.2.3' -RepositoryPath $source -OutputPath $first } 'already exists'
Test-Exception { & $builder -Version '1.2.3' -RepositoryPath $source -OutputPath (Join-Path $fixtureRoot 'invalid-url') -ChecklistUrl 'http://example.com/123' } 'GitHub issue URL'
$nativeChangelog = (Get-Content -LiteralPath (Join-Path $source 'CHANGELOG.md') -Raw).Replace('## [1.2.3](https://example.com/current)', '## 1.2.3')
Set-Content -LiteralPath (Join-Path $source 'CHANGELOG.md') -Value $nativeChangelog
git -C $source add -- CHANGELOG.md
git -C $source -c user.name=Fixture -c user.email=fixture@example.invalid -c commit.gpgsign=false commit --quiet -m 'test: native release heading fixture'
$nativeOutput = Join-Path $fixtureRoot 'native-headings'
& $builder -Version '1.2.3' -RepositoryPath $source -OutputPath $nativeOutput
$nativeNotes = Get-Content -LiteralPath (Join-Path $nativeOutput "$prefix.notes.md") -Raw
Test-Condition ($nativeNotes -match 'Native category notes' -and $nativeNotes -match 'Current breaking details' -and $nativeNotes -notmatch 'Older notes|Future notes') 'native unlinked version heading retains only its release categories'
Set-Content -LiteralPath (Join-Path $source 'CHANGELOG.md') -Value '# Changelog without release notes'
git -C $source add -- CHANGELOG.md
git -C $source -c user.name=Fixture -c user.email=fixture@example.invalid -c commit.gpgsign=false commit --quiet -m 'test: missing release notes fixture'
+1 -1
View File
@@ -1 +1 @@
0.1.2
1.0.0