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
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ jobs:
run: uv export --frozen --no-dev --no-emit-project --format requirements.txt --output-file .ci-audit-requirements.txt

- name: Audit dependencies
run: uv run pip-audit -r .ci-audit-requirements.txt
run: uv run python scripts/run_dependency_audit.py -r .ci-audit-requirements.txt

validate-fingerprints:
runs-on: ubuntu-latest
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
name: CodeQL

on:
push:
branches: ["main"]
pull_request:
branches: ["main"]
schedule:
Expand Down
10 changes: 5 additions & 5 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ jobs:
run: uv export --frozen --no-dev --no-emit-project --format requirements.txt --output-file .ci-audit-requirements.txt

- name: Audit dependencies
run: uv run pip-audit -r .ci-audit-requirements.txt
run: uv run python scripts/run_dependency_audit.py -r .ci-audit-requirements.txt

build:
# Pure build job. The supply-chain isolation contract requires
Expand Down Expand Up @@ -403,17 +403,17 @@ jobs:
mkdir -p sbom
# ``--no-deps`` tells pip-audit not to install the locked
# requirements again; it just reads the requirements text
# to produce the SBOM. Exit 1 means findings and is accepted here
# because the enforcing audit runs in ``test``. Any higher status,
# missing/corrupt JSON, or missing project root fails publication.
# to produce the SBOM. Every nonzero status blocks publication,
# including a finding first disclosed after the enforcing audit ran.
# Missing/corrupt JSON or a missing project root also fails closed.
set +e
uv tool run pip-audit -r .ci-audit-requirements.txt \
--format=cyclonedx-json \
--output "sbom/recon-tool-${VERSION}.cdx.json" \
--no-deps
audit_status=$?
set -e
if [ "$audit_status" -gt 1 ]; then
if [ "$audit_status" -ne 0 ]; then
exit "$audit_status"
fi
test -s "sbom/recon-tool-${VERSION}.cdx.json"
Expand Down
28 changes: 28 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,34 @@ operator, corporate group, ownership, or control.

## [Unreleased]

## [2.6.6] - 2026-07-18

### Tool Surface Changes

Tool surface changes: no CLI command or flag changes.

### Changed

- CodeQL now analyzes every push to `main` in addition to pull requests,
scheduled scans, and manual runs, keeping exact default-branch evidence
current without a release-time dispatch.
- Roadmap, OpenSSF, supply-chain, security, and release guidance now separates
shipped behavior, durable policy, and volatile external evidence without
pinning current trust claims to an obsolete release or sample size.

### Fixed

- Enforcing CI and release dependency audits now retry exactly once only for a
strict allowlist of recognized transport failures. Vulnerability findings,
malformed input, unknown failures, and exhausted retries remain nonzero and
keep publication blocked.
- The dependency-audit runner now resolves the installed auditor under Python
isolated mode, preventing checkout files from shadowing the blocking audit
module. The later isolated SBOM job now blocks on every nonzero audit status,
including a finding first disclosed after the enforcing audit ran.
- The latest security-audit resolution now records v2.6.4 as its actual closure
release instead of retaining an Unreleased label.

## [2.6.5] - 2026-07-18

### Tool Surface Changes
Expand Down
30 changes: 17 additions & 13 deletions ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,26 @@ The canonical roadmap lives in [docs/roadmap.md](docs/roadmap.md).

Current status: recon v2.6.5 has a stable, production-ready baseline. The CLI,
JSON schema, local stdio MCP server, bounded collectors, generated-artifact
guards, validation gates, and release path are shipped. Unreleased work improves
first-run help and error recovery, removes rejected MCP values from logs,
guards, validation gates, and release path are shipped. Recent shipped work
improves first-run help and error recovery, removes rejected MCP values from
logs,
aligns public issue intake with the no-target-data policy, corrects the package
surface description, requires verification dates on new fingerprint
detections, groups batch help by task, redacts unexpected batch exception
details, reports cache deletion failures, exposes payload-free result and CT
cache metadata, hardens diagnostic rendering, discloses doctor connectivity,
keeps narrow help complete, adds all-source failure recovery, executes the
sealed wheel before publication, and treats closed output pipes as normal
control flow. Current polish also makes catalog search bounded and
control flow. Shipped polish also makes catalog search bounded and
field-associated, preserves every same-slug fingerprint record in `show`,
aligns CLI and MCP category matching, keeps signal summary projections
consistent, emits plain redirected gate logs, and makes partial-release reruns
stop on named preconditions before mutation. The current maintenance pass also
stop on named preconditions before mutation. Shipped maintenance also
bounds default cache payload inspection with exact completeness counts, exposes
and clears interrupted-write residue, separates fingerprint corpus errors from
misses under bounded input, rejects empty catalog filters, preserves ranked
signal hierarchy, and keeps welcome descriptions associated at 80 columns.
The current MCP diagnostics pass makes static registry failures exit non-zero,
The shipped MCP diagnostics pass makes static registry failures exit non-zero,
exercises every canonical local JSON resource through live stdio, preserves the
failed protocol phase, and documents the three-part server and client check.
The active product work is evidence-semantic integrity and a
Expand All @@ -37,7 +38,7 @@ follow-up work. The first bounded internal claim contract now evaluates the exac
DMARC `p=reject` observation and drives the opt-in cohort schema 2.2 DMARC
denominator while schema 2.1 remains the default.

Unreleased maintenance also refuses HTTP destinations whose hostname or DNS
Shipped maintenance also refuses HTTP destinations whose hostname or DNS
result cannot be validated, confines default MCP logging to the running server,
bounds fatal MCP stderr, keeps doctor checks useful across ordinary HTTP
request failures, labels CT cache overview freshness, and completes narrow root
Expand All @@ -51,18 +52,21 @@ validates the completed SBOM, exported provenance bundle, exact signer workflow,
source tag and commit digest, and cross-channel digests. Reviewed installer
helpers bind installation to their release version
and preserve one existing package-manager owner; consumer verification now has
complete fail-closed POSIX and PowerShell paths. Current release hardening also
complete fail-closed POSIX and PowerShell paths. Shipped release hardening also
reuses one bounded exact PyPI metadata validator, includes the completed SBOM in
GitHub provenance, and rejects unsafe existing-release state before recovery
can replace assets. The operator recovery block itself now uses strict Bash
mode, named failures, and a visible pre-mutation success checkpoint. One
digest-bound v2.6.3 historical exception preserves the
published distribution-only bundle while still requiring SBOM validation;
future releases fail if SBOM provenance is absent.
digest-bound v2.6.3 historical exception preserves the
published distribution-only bundle while still requiring SBOM validation;
future releases fail if SBOM provenance is absent. Enforcing dependency
audits resolve the installed auditor under Python isolated mode, and the
later SBOM job treats every nonzero audit status as fatal.

Pull requests targeting `main` now run CodeQL before merge while scheduled and
manual default-branch scans remain available. Remote readiness keeps the other
required code-owned Scorecard controls at `10` and uses the observed SAST floor
Pull requests targeting `main` run CodeQL before merge, every push to `main`
updates exact default-branch analysis, and scheduled and manual scans remain
available. Remote readiness keeps the other required code-owned Scorecard
controls at `10` and uses the observed SAST floor
of `7` while the public sample still contains pre-policy pull requests. The
SAST requirement returns to `10` only after the public API reports supported
SAST checks for every sampled merged pull request; historical checks are not
Expand Down
2 changes: 1 addition & 1 deletion docs/assurance-case.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ matched environment. Full details and the bounded recipe are in
| A remote readiness report cannot combine current-main CI with an older or moved published package: the remote and local current project-version tag plus `HEAD` must resolve to the same full commit | `release_readiness::_check_release_tag_binding`; `test_release_readiness::test_remote_release_tag_binding_rejects_mixed_head_and_release_state`; `test_release_readiness::test_remote_release_tag_binding_rejects_moved_public_tag` |
| Local release transaction starts from clean current `main`, synchronizes exact-version installer helpers with other owned surfaces, stages only those surfaces, creates one reviewed tag, and restores files, index, commit, and owned tag when the mutation transaction fails | `test_release_script::test_release_surface_generation_updates_installers_and_artifacts`, `test_release_script::test_release_transaction_owns_both_installer_helpers`, `test_release_script::test_release_push_command_names_only_the_reviewed_tag`, `test_release_script::test_release_rollback_restores_files_index_commit_and_owned_tag`, `test_install_scripts::test_installers_bind_the_reviewed_release_version_and_owner` |
| A release tag must match the project version and dated nonempty changelog section, identify the workflow SHA, and be contained in freshly fetched current `main` before the test gate can run | `test_validate_release_tag::test_matching_tag_with_notes_and_main_ancestry_passes`, `test_validate_release_tag::test_mismatched_tag_fails_before_git`, `test_validate_release_tag::test_non_main_tag_fails`, `test_release_workflow::test_release_preflight_blocks_mismatched_or_non_main_tags` |
| CycloneDX output contains the exact versioned project root and complete dependency edge; publication and remote verification fail rather than repairing malformed or non-UTF-8 completed evidence | `test_finalize_sbom::test_finalize_sbom_adds_project_root_and_dependency_edge`, `test_finalize_sbom::test_completed_sbom_validation_fails_without_repair`, `test_finalize_sbom::test_completed_sbom_validation_rejects_non_utf8_bytes`, `test_release_workflow::test_pypi_publication_waits_for_valid_sbom`, `test_release_workflow_contract::TestGithubReleaseAttachesBothArtifacts` |
| CycloneDX generation rejects every nonzero audit status; output contains the exact versioned project root and complete dependency edge; publication and remote verification fail rather than repairing malformed or non-UTF-8 completed evidence | `test_run_dependency_audit::test_enforcing_workflows_use_bounded_audit_runner`, `test_release_workflow_contract::TestSbomJobIsIsolated::test_sbom_job_validates_complete_project_bom`, `test_finalize_sbom::test_finalize_sbom_adds_project_root_and_dependency_edge`, `test_finalize_sbom::test_completed_sbom_validation_fails_without_repair`, `test_finalize_sbom::test_completed_sbom_validation_rejects_non_utf8_bytes`, `test_release_workflow::test_pypi_publication_waits_for_valid_sbom` |
| GitHub verification for releases produced by the current workflow binds the wheel, sdist, and completed SBOM to the exported bundle, exact release workflow, source tag, commit digest, and hosted-runner boundary. The exact v2.6.3 historical exception requires wheel and sdist provenance plus completed SBOM structure validation; PyPI uses pinned PEP 740 verification and OIDC Trusted Publishing | `test_release_readiness::test_github_attestations_verify_wheel_sdist_and_sbom`; `test_release_readiness::test_github_attestations_preserve_exact_v263_legacy_subject_boundary`; `release.yml` (`attest`, `publish-pypi` jobs); `test_release_workflow_contract::TestAttestationJob` |
| Existing GitHub Release recovery permits asset replacement only for the exact published tag and title with mutable, expected-only inventory, complete or partial; draft, prerelease, immutable, duplicate, unexpected, and malformed state fails before `--clobber`, and the remote tag must still resolve to the workflow SHA | `check_release_recovery::validate_release_recovery`; `test_release_recovery::test_unsafe_existing_release_state_is_rejected`; `test_release_workflow_contract::TestGithubReleaseAttachesBothArtifacts` |

Expand Down
23 changes: 12 additions & 11 deletions docs/openssf-posture.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ while the Best Practices Badge is an account-backed project questionnaire.
## Current Snapshot

- Date checked: 2026-07-18.
- Live API state at check time: public API rechecked for the exact `HEAD` commit
that published v2.6.4 after CI and publication completed.
- Live API state at check time: public API rechecked for the exact current
release `HEAD` after CI and publication completed.
- Scorecard version: `v5.3.0`.
- Score: `8.2`.
- Public source: `https://api.securityscorecards.dev/projects/github.com/blisspixel/recon`.
Expand All @@ -24,22 +24,23 @@ The measured non-SAST code-owned controls are green: dangerous workflow
patterns, dependency update automation, token permissions, pinned dependencies,
binary artifacts, security policy, known vulnerabilities, packaging, fuzzing,
signed releases, and license all score `10`. CI-Tests also scores `10` because
all 17 sampled merged pull requests ran CI. SAST scores `7`: CodeQL is detected,
but the same 17 pull-request heads predate PR-scoped CodeQL and have no supported
SAST check. A scheduled or manual scan of `main` does not rewrite that history.
New pull requests targeting `main` now run CodeQL while the weekly and manual
main analysis remains available. The SAST gate can return to `10` only after the
every sampled merged pull request ran CI. SAST scores `7`: CodeQL is detected,
but the sample still includes merged pull-request heads from before PR-scoped
CodeQL was required. Current pull requests provide supported SAST evidence; a
default-branch scan does not rewrite older pull-request history. Pull requests
targeting `main` and pushes to `main` run CodeQL, while weekly and manual main
analysis remains available. The SAST gate can return to `10` only after the
public API reports successful supported SAST checks for every merged pull
request in its sampled window. The exact overall score remains a dated external
snapshot; the enforced `8.0` overall and `7` SAST floors are the current
regression policy.
request in its sampled window. The exact sample size and overall score remain
dated external state; the enforced `8.0` overall and `7` SAST floors are the
current regression policy.

The remaining low or unknown checks are process-bound:

| Check | Current reason | Real next step |
|---|---|---|
| `Branch-Protection` | `main` is protected, but administrator bypass remains and PRs are not mandatory. | Keep the current required-check ruleset for clean-main work. If the project moves to a multi-maintainer flow, require PRs, remove administrator bypass, and require CODEOWNERS review. |
| `Code-Review` | No reviewed pull-request history exists for Scorecard to evaluate. | Use normal reviewed PRs for non-urgent work when there is another qualified reviewer. Do not manufacture review history. |
| `Code-Review` | No human-approved changesets appear in Scorecard's sampled window; automated review comments are not approvals. | Use normal reviewed PRs for non-urgent work when there is another qualified reviewer. Do not manufacture review history. |
| `CII-Best-Practices` | No OpenSSF Best Practices Badge project is linked. | Use [openssf-badge-readiness.md](openssf-badge-readiness.md) to answer the questionnaire from committed evidence; link a badge only after the real project page exists and the answers match the repository. |
| `Contributors` | Scorecard sees no contributor diversity across organizations. | Accept this for a single-maintainer project. Do not add artificial contributors. |

Expand Down
11 changes: 7 additions & 4 deletions docs/release-process.md
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,10 @@ Triggered by any tag matching `v*` pushed to the repo. The workflow:
2. **test**: after preflight, runs the exact stable and candidate MCP SDK matrix,
then delegates to the complete `scripts/check.py` gate with added-line text
hygiene covering the full previous-release-to-tag range. A separate
hash-pinned runtime requirements export is audited with `pip-audit`.
hash-pinned runtime requirements export is audited with `pip-audit`. The
audit runner resolves the installed auditor under Python isolated mode and
retries once only for recognized transport failures; findings, unknown
failures, and a second transport failure remain fatal.
3. **build**: after `test`, exact uv 0.11.17 uses the hash-locked build graph to
produce one sdist, constructs one wheel from that exact sdist, and
immediately seals both under `dist/`. Main CI runs the same sequence twice,
Expand All @@ -221,9 +224,9 @@ Triggered by any tag matching `v*` pushed to the repo. The workflow:
`python -m recon_tool --version` from the wheel in isolated environments.
5. **sbom**: after `test`, generates the CycloneDX release SBOM independently of
the package build path, adds the project root and dependency edge, and
validates the resulting document. Findings may coexist with this artifact
because the enforcing dependency audit already ran in `test`; SBOM tool or
validation failure is fatal.
validates the resulting document. Every nonzero audit status is fatal here,
including a finding first disclosed after `test`; SBOM tool or validation
failure is also fatal.
6. **attest**: after `build` and `sbom`, records one GitHub provenance
attestation whose subject set contains the wheel, sdist, and completed SBOM.
7. **export-attestations**: after `build`, `sbom`, and `attest`, exports the
Expand Down
Loading