Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,19 +88,19 @@ Not applicable:

One badge criterion was considered during the 2026-07-30 Passing alignment and declined, so nobody re-opens it as an oversight. `documentation_interface` asks a project to provide reference documentation describing the external interface of what it produces. Upstream it is a MUST with `na_allowed` and a justification required, so a project may mark it not applicable. No rule was written for it, for three reasons. Any check this project could write reduces to a file that looks like API documentation existing, which is unobservable in the way `STANDARD.md` requires. R-CHG-02 already forces the public API to be declared, which is the half a reader needs to know what is covered. And no skill in the kit owns reference documentation, so a rule would have no `Fixed by:` line to name.

R-COM-08 is met by the Governance section of `CONTRIBUTING.md`: one maintainer decides and releases, only the maintainer has write access so every contribution arrives from a fork, and no succession is arranged. The rule is deliberately satisfiable by a solo project stating that it is one, so the section says that plainly rather than inventing a governance structure. It also records the one decision that is not a judgement call, that a rule changes when there is an upstream source for the change. R-COM-07 is met: the description, the homepage, and eight topics are set, all three readable with `gh repo view --json description,homepageUrl,repositoryTopics`. They are forge settings, so no file records them and no diff shows them changing. The social preview image is still unset, and R-COM-07 does not ask for one.
R-COM-08 is met by the Governance section of `CONTRIBUTING.md`: one maintainer decides and releases, only the maintainer has write access so every contribution arrives from a fork, and no succession is arranged. The rule is deliberately satisfiable by a solo project stating that it is one, so the section says that plainly rather than inventing a governance structure. It also records the one decision that is not a judgement call, that a rule changes when there is an upstream source for the change. R-COM-07 is met: the description, the homepage, and eight topics are set, all three readable with `gh repo view --json description,homepageUrl,repositoryTopics`. They are forge settings, so no file records them and no diff shows them changing. The social preview image is set, and R-COM-07 does not ask for one.

R-CHG-03 was pending, until 0.2.0 was tagged: three plugin manifests and the newest `CHANGELOG.md` heading said 0.2.0 while the newest tag said `v0.1.0`. Because nothing had ever consumed 0.2.0, the fix folded the accumulated `Unreleased` section into it and dated it at the tag, rather than tagging an old commit and opening 0.3.0. Folding a release that never shipped means dropping every entry that describes a change to something introduced in the same release: the site landed in 0.2.0, so the entries tuning its page titles, sidebar, and search index described a state no user ever saw.

The public API that R-CHG-02 requires is declared in the README under Versioning: skill names and paths, rule IDs and what they require, the validator's command line, and the manifest paths each host reads. A skill body's wording and the `references/` layout are deliberately outside it, so prose can be improved in a patch. Tightening what a rule requires is incompatible, because a repository that passed yesterday fails today.

`CHANGELOG.md` pins Keep a Changelog 2.0.0, which was published on 2026-06-07 and is what `oss-changelog` prescribes. The file used to pin 1.1.0.

R-SEC-05 is met: `git cat-file -t v0.2.0` prints `tag`, and `git tag -v v0.2.0` reports a good SSH signature for the ED25519 key published at `gh api users/svyatov/ssh_signing_keys`. Verification needs `gpg.ssh.allowedSignersFile` pointed at an allowed-signers file naming that key with the tagger's email; without it `git tag -v` fails with a configuration error rather than a bad signature, which reads like an unsigned tag and is not one. The rule now carries both the fetch command and that distinction, so an audit on a fresh checkout can resolve it instead of reporting unknown.
R-SEC-05 is met: `git cat-file -t v0.13.0` prints `tag`, and `git tag -v v0.13.0` reports a good SSH signature for the ED25519 key published at `gh api users/svyatov/ssh_signing_keys`. Verification needs `gpg.ssh.allowedSignersFile` pointed at an allowed-signers file naming that key with the tagger's email; without it `git tag -v` fails with a configuration error rather than a bad signature, which reads like an unsigned tag and is not one. The rule now carries both the fetch command and that distinction, so an audit on a fresh checkout can resolve it instead of reporting unknown.

R-SEC-04 and R-SEC-09 were both pending on the repository becoming public. It is public now, and both are set. The default branch is guarded by a repository ruleset named `main`, scoped to `~DEFAULT_BRANCH` rather than the literal branch name so renaming the branch cannot unguard it. It requires a pull request, resolved review threads, squash as the only merge method, and the four checks CI reports, and it rejects force pushes, deletion, and non-linear history. It requires no approving review, because R-SEC-12 does not reach a repository with one principal and R-SEC-04 no longer asks for one. Expect `Branch-Protection` in an OpenSSF Scorecard result to read 3 of 10 for that reason: its tiers gate each other, review is tier 2, and status checks are tier 3. That is the intended state, not a regression. Read the ruleset with `gh api repos/svyatov/oss-kit/rulesets`, not with the classic `branches/main/protection` endpoint: this repository has no classic branch protection rule, so that endpoint answers `404 Branch not protected`, which reads like an unguarded branch and is not one. Code scanning runs through CodeQL default setup rather than a workflow file, scanning `actions`, `javascript`, `javascript-typescript`, and `typescript`, so nothing under `.github/workflows/` implements it and nothing there needs maintaining for it.
R-SEC-04 is met, and R-SEC-09 is open for fork pull requests. The default branch is guarded by a repository ruleset named `main`, scoped to `~DEFAULT_BRANCH` rather than the literal branch name so renaming the branch cannot unguard it. It requires a pull request, resolved review threads, squash as the only merge method, and the four checks CI reports, and it rejects force pushes, deletion, and non-linear history. It requires no approving review, because R-SEC-12 does not reach a repository with one principal and R-SEC-04 no longer asks for one. Expect `Branch-Protection` in an OpenSSF Scorecard result to read 3 of 10 for that reason: its tiers gate each other, review is tier 2, and status checks are tier 3. That is the intended state, not a regression. Read the ruleset with `gh api repos/svyatov/oss-kit/rulesets`, not with the classic `branches/main/protection` endpoint: this repository has no classic branch protection rule, so that endpoint answers `404 Branch not protected`, which reads like an unguarded branch and is not one. Code scanning runs through CodeQL default setup rather than a workflow file, scanning `actions`, `javascript`, `javascript-typescript`, and `typescript`, so nothing under `.github/workflows/` implements it. GitHub excludes fork pull requests from default setup, while this repository routes every external contribution through a fork. Default setup therefore does not meet R-SEC-09 on that path.

The ruleset also carries a `code_scanning` rule requiring CodeQL at `security_alerts_threshold: high_or_higher` and `alerts_threshold: errors`, and a `code_quality` rule at `severity: errors`. These block on what the analysis found, where the `CodeQL` entry in `required_status_checks` only blocks on whether it reported. The `code_quality` rule is the one to watch: GitHub Code Quality is a licensed product enabled per repository, it is absent from the rulesets REST reference, and it exposes no endpoint that reports whether it is on for this repository, so `gh api repos/svyatov/oss-kit/code-quality` answering `Not Found` says nothing either way. If a pull request ever sits unmergeable on a code quality result that never arrives, that rule is the first thing to check.
The ruleset also carries a `code_scanning` rule requiring CodeQL at `security_alerts_threshold: high_or_higher` and `alerts_threshold: errors`, and a `code_quality` rule at `severity: errors`. These block on what the analysis found, where the `CodeQL` entry in `required_status_checks` only blocks on whether it reported. The `code_quality` rule is a live gap. GitHub now documents `GET /repos/{owner}/{repo}/code-quality/setup`, and the read reports that Code Quality is unavailable for this repository. Remove the rule from `main` before relying on it as a merge gate.

A ruleset has no "Do not allow bypassing the above settings" checkbox; the equivalent is its bypass list, and this one is empty. Nothing here exempts the owner from anything. It used to hold `RepositoryRole` `5`, Repository admin, at `pull_request` mode, which existed for one reason: the ruleset required an approving review, nobody can approve their own pull request, and without the exemption every change the sole maintainer opened was unmergeable. Dropping the review requirement removed the reason, so the entry went too, and the ruleset now binds the owner as tightly as anybody else. Confirm it from the create or read response, where `bypass_actors` reads `[]` and `current_user_can_bypass` reads `never`. Do not restore the entry to unblock a merge; a required approval nobody can give, undone by an exemption for the only person it named, is strictly weaker than not requiring it.

Expand All @@ -110,7 +110,7 @@ R-SEC-13 is met by two more rulesets on `refs/tags/v*`, read back with `gh api r

R-SEC-10 is met, read back rather than inferred from the write: `gh api repos/svyatov/oss-kit --jq .security_and_analysis` reports `secret_scanning` and `secret_scanning_push_protection` both `enabled`. The same response reports `secret_scanning_non_provider_patterns` and `secret_scanning_validity_checks` disabled, which is correct rather than a gap, because both belong to GitHub Secret Protection, a paid product the rule's `Check` places outside itself. Read that field with an admin token. For a caller without admin the key is absent from the response entirely, which reads as disabled and is not.

R-SEC-11 is met on coverage and on reporting, and open on blocking. `gh api repos/svyatov/oss-kit/vulnerability-alerts -i` answers 204 and `dependabot_security_updates` reads `enabled`, but the dependency graph does not parse `bun.lock`, so `gh api repos/svyatov/oss-kit/dependency-graph/sbom` lists 10 `pkg:npm` packages against the 492 that `site/bun.lock` resolves, alongside 8 `pkg:githubactions` and 1 `pkg:github`. The `osv-scanner` workflow covers that residual: its `pull-request` job scans what the change introduces, and its `scheduled` job scans the repository weekly and reports.
R-SEC-11 is met on coverage and on reporting, and open on blocking. `gh api repos/svyatov/oss-kit/vulnerability-alerts -i` answers 204 and `dependabot_security_updates` reads `enabled`, but the dependency graph does not parse `bun.lock`, so `gh api repos/svyatov/oss-kit/dependency-graph/sbom` lists 10 `pkg:npm` packages against the 469 that `site/bun.lock` resolves, alongside 8 `pkg:githubactions` and 1 `pkg:github`. The `osv-scanner` workflow covers that residual: its `pull-request` job scans what the change introduces, and its `scheduled` job scans the repository weekly and reports.

What is open is the blocking half. The `main` ruleset requires four checks and `pull-request / osv-scan` is not among them, so the scan reports today and does not gate. It stays that way until a pull request from a fork runs it green, because a fork gets a read-only token while the called workflow declares `security-events: write` at its own top level. If GitHub compares the fork-capped grant rather than the declared one, that job fails at startup, produces no check run at all, and a required check that never reports blocks the merge with nothing to read. One green fork run settles it, and adding the `pull-request / osv-scan` context to the ruleset's `required_status_checks` closes the rule. Add it there and not to `code_scanning_tools`, which holds CodeQL and takes analysis tools rather than check contexts.

Expand Down
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ The format is based on [Keep a Changelog 2.0.0](https://keepachangelog.com/en/2.

## [Unreleased]

### Fixed

- `oss-harden` pins the `uses:` entries a shipped composite action carries, not the workflow lines alone. A repository whose `action.yml` names an unpinned action passed Step 3 with a mutable reference left in place.
- `oss-harden` says a Dependabot cooldown below three days shortens the delay. Step 5 said such a value replaced the platform default with something slower, which is the reverse of what it does.
- `oss-harden` sends a repository that receives fork pull requests to CodeQL advanced setup. GitHub excludes fork pull requests from default setup, so recommending it left R-SEC-09 open on the path every external contribution takes.

## [0.13.0] - 2026-08-07

### Changed
Expand Down
Loading
Loading