Skip to content

kit 6.6.5

Choose a tag to compare

@github-actions github-actions released this 19 Aug 14:37
· 52 commits to main since this release
e8909b2

Changed

  • Token publishing is now disallowed on all 13 packages. With 6.6.5-rc.1 proving the OIDC
    path, every package's npm "Publishing access" moved to require two-factor authentication and
    disallow bypass 2fa tokens
    — the setting that turns "we also have OIDC" into "only OIDC can
    publish". Each was read back from its own settings page. There is no token fallback left, so
    a misconfigured trusted publisher now fails that package's publish step and is fixed in npm's
    UI rather than worked around; the workspace loop is idempotent, so re-running completes the
    set. docs/RELEASING.md records the operational cost: npm's step-up 2FA is flaky under
    repetition, Update Package Settings is a no-op unless the radio actually changed, and the
    CLI equivalent (npm access set mfa=publish) needs a TOTP code that a security-key-only
    account cannot produce.

  • Publishing runs on trusted publishing (OIDC); the npm token is gone. All 13 packages
    now carry a GitHub Actions trusted publisher naming sandstream/kit, publish.yml and the
    npm-publish environment, with the single permission npm publish — not staged publish,
    because the job publishes directly and least privilege is the point. NODE_AUTH_TOKEN is
    removed from both publish steps, which is what actually switches the credential: npm prefers
    a token whenever one is present, so the two cannot both be in effect. That also makes a
    re-added token a SILENT downgrade back to the credential npm is retiring, so
    src/publish-workflow.test.ts now fails if any executing line in the workflow carries one.
    The environment is no longer merely the guard around a secret — it is part of the credential,
    since npm only mints one for runs that reach it.

    Two things worth knowing for the next package, both learned the hard way: npm requires
    step-up 2FA (security key) per save and does not keep the session elevated, so it is one
    key tap per package and a timed-out prompt loses that save (nothing half-saves — retrying is
    free); and driving the UI quickly trips Cloudflare's bot check, which only a human can clear.

Added

  • The install-script grants check now asks whether the granted package was ever
    triaged.
    #475 deferred this on the grounds that no triage ledger existed to consult. That
    was wrong: .kit-triage.jsonl has been there all along — kit triage appends a PASS to it
    and the pre-commit check-deps gate reads it. The shape audit can only say how BROAD a
    grant is; this asks the question shape cannot answer, and the answers rank differently. A
    pinned grant for a package nothing has ever evaluated now warns at high, above an
    unpinned grant for one kit cleared yesterday. A grant whose only triage is older than the
    freshness window is reported separately, because "reviewed a month ago" and "never reviewed"
    are not the same claim. TRIAGE_MAX_AGE_DAYS is now exported and imported rather than
    copied, so check-deps and this check cannot drift to different windows.

    When the log is absent — most repos have never run kit triage — the row says the
    cross-check did not run instead of reporting every grant as untriaged. A confident finding
    built on an absent file is the failure mode this codebase keeps catching in itself. A torn
    append is skipped rather than allowed to fail the check.

  • scripts/trusted-publishing-wizard.sh — the npm OIDC migration, walked. The
    registry side of trusted publishing can only be done in a browser, one package at a time:
    npm allows exactly one trusted publisher per package, and npm access on 11.19.0 covers
    status/collaborators/grant/revoke with no read or write path for the setting. So the wizard
    is honest about what it cannot verify and rigorous about the rest. It derives the package
    list from the repo rather than carrying a copy — a hardcoded list drifts the moment someone
    adds a plugin, and a missed package is the one failure this migration must not have, since
    no token plus no trusted publisher means that package's publish step fails. Progress is
    recorded in .kit/trusted-publishing.state, so it is resumable across sessions; the
    token-removal stage refuses to run until every package is confirmed; and the final stage
    points at a prerelease (routed to next, so latest cannot move) as the only honest test.
    Its publish.yml edit is fail-closed — it removes the parent env: when the token was its
    only child, refuses if any executing line still references NPM_TOKEN, and lists the
    comment lines that still describe the token so the prose is fixed in the same PR.

  • A dialect gate for that script (src/wizard-script.test.ts). Nothing in this repo lints
    shell — lint and format:check are TypeScript-only and CI has no shellcheck — so a
    committed wizard could be broken in ways no gate notices until the human runs it, during a
    credential migration, once. Two such bugs were found by hand while writing it and are now
    pinned: mapfile (a bash 4 builtin; macOS ships bash 3.2.57 and the publishing machine is a
    Mac) and sed -E '…+?…' (POSIX ERE has no lazy quantifier — macOS sed exits with
    "repetition-operator operand invalid"). Both parse fine to the eye and to bash -n. The
    gate was mutation-tested by reintroducing each.

Fixed

  • A second gate that could not fail: the security-headers step. security.yml read
    if grep -rE "Content-Security-Policy|…" src/ packages/ | head -1; thenhead exits 0 on
    empty input, so the condition was always true, Security headers configured printed
    unconditionally, and the ::warning:: branch was unreachable. Measured against a
    guaranteed-absent pattern before fixing. Same class as the 6.6.4 tag-signature gate, and
    found by the rule that fix installed.
  • R1-fail-open-ci now names the class instead of the instance. It flagged a condition
    piped into tee or cat; it now flags any condition whose pipeline ENDS in a status-blind
    sink (tee, cat, head, tail, wc, sort, uniq, tr). grep -q stays allowed —
    there the pipe's last command IS the question. Adding the rule made kit's own self-audit
    fail on security.yml, which is how the second instance surfaced.

Changed

  • The security-headers check is now honest, not merely correct. kit is a CLI + MCP (stdio)
    tool — the same "no HTTP surface" that skips Stage 3 DAST — so a missing CSP is not a
    finding here, and a permanently-expected warning would only train people to ignore warnings.
    The check is gated on an HTTP surface actually existing (createServer, express(,
    fastify(, Bun.serve, .listen( — each matching 0 lines when written) and fires the day
    one lands. All three branches were driven in a fixture tree; the two that mattered had never
    been reachable.

Full changelog: https://github.com/sandstream/kit/blob/v6.6.5/CHANGELOG.md

Verify this release:

git tag -v v6.6.5
npm audit signatures