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
35 changes: 35 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -135,8 +135,13 @@ jobs:
name: Create GitHub Release
needs: [build-release]
runs-on: ubuntu-latest
# Least privilege, scoped to this job only:
# contents: write -> create the GitHub Release and upload assets
# id-token: write -> mint the GitHub OIDC token cosign exchanges for a
# short-lived Sigstore (Fulcio) signing certificate (#591)
permissions:
contents: write
id-token: write

steps:
- uses: actions/checkout@v6
Expand All @@ -156,6 +161,36 @@ jobs:
sha256sum * > checksums-sha256.txt
cat checksums-sha256.txt

# Keyless signing (#591). Ordering matters: sign BEFORE creating the
# release. If signing fails the job stops here and no release is
# published, so we never ship a release that lacks its signature.
# One signature over the checksums file covers every archive, because
# install.sh / `cora upgrade` verify each archive against that file.
# sigstore/cosign-installer v3.10.1, pinned by commit SHA.
- name: Install cosign
uses: sigstore/cosign-installer@7e8b541eb2e61bf99390e1afd4be13a184e9ebc5
with:
cosign-release: 'v2.6.5'

- name: Sign checksums with cosign (keyless)
working-directory: release-artifacts
run: |
cosign sign-blob --yes \
--bundle checksums-sha256.txt.sigstore.json \
checksums-sha256.txt
test -s checksums-sha256.txt.sigstore.json

# Fail the job (before publishing) if the bundle does not verify against
# this exact workflow identity.
- name: Verify the fresh signature
working-directory: release-artifacts
run: |
cosign verify-blob \
--bundle checksums-sha256.txt.sigstore.json \
--certificate-identity "https://github.com/${GITHUB_REPOSITORY}/.github/workflows/release.yml@${GITHUB_REF}" \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
checksums-sha256.txt

- name: Extract version from tag
id: version
run: |
Expand Down
1 change: 1 addition & 0 deletions AGENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,7 @@ After release completes, verify:
- [ ] GitHub Release page shows vX.Y.Z with correct changelog
- [ ] 4 platform binaries attached to release
- [ ] SHA256 checksums file included
- [ ] `checksums-sha256.txt.sigstore.json` asset exists and verifies (`cosign verify-blob` command in `docs/installation.md#verify-release-signatures`); `CORA_REQUIRE_SIGNATURE=1 CORA_VERSION=vX.Y.Z sh install.sh` succeeds
- [ ] `crates.io` shows new version: `cargo search cora-code`
- [ ] `codecora.dev` reflects new docs
- [ ] Marketplace action still works (test on a test PR)
Expand Down
25 changes: 24 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,28 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [0.18.0] - 2026-10-09

### Security

- **Release checksums are signed with cosign keyless and verified by `install.sh` and `cora upgrade` (#591).** `release.yml` now signs `checksums-sha256.txt` (which covers every archive) via Sigstore/GitHub OIDC before creating the release and uploads `checksums-sha256.txt.sigstore.json`; a signing failure aborts the job so no unsigned release is published. `install.sh` and `cora upgrade` verify the bundle with `cosign verify-blob` (identity: this repo's `release.yml` on a `v*` tag, issuer `token.actions.githubusercontent.com`) when `cosign` is on `PATH`; an invalid signature aborts. Without `cosign`, or for releases without a bundle, they print a notice and fall back to checksum-only. `CORA_REQUIRE_SIGNATURE=1` makes a missing `cosign` or bundle fatal. See `docs/installation.md`.

### Changed

- **Documented what the hardcoded-secret check flags and its one known limit** (all-letter unquoted YAML values), and closed that item as not planned (#625, #635).

- **Scanners take file lines instead of diffs (#610).** New `engine::scan_input::ScanFile` (`path`, `language`, `(line number, text)` pairs) is the one input shape for `secrets_scanner::scan_secrets_in`, `security_scanner::scan_security_in`, `rules::run_rules_in` and `inline_suppress::apply_lines`. Diff chunks and whole files are thin adapters (`ScanFile::added_lines`, `ScanFile::post_image`, `ScanFile::from_entry`); `scan_secrets`/`scan_security`/`run_rules`/`inline_suppress::apply` keep their signatures. The `files_as_chunks` workaround and the MCP fake diff are gone. Findings are byte-identical for diffs and scans. Unit tests that open an index session now use a per-test temp database instead of the shared global one.

### Fixed

- **A `rules_engine:` section without `enabled: true` no longer disables the built-in rules.** `enabled` now defaults to `true` when the section is present, so `rules_engine: { max_findings: 0 }` only changes the cap; set `enabled: false` to turn the engine off (#638).

- **`cora.check_snippet` (MCP) now honors `ignore.rules`, inline `cora-ignore:` markers and the false-positive filters, and `rules_engine.max_findings` (#610).** The tool used to run the secrets and security scanners on a fake diff with a fixed cap of 10 per scanner and no filtering, so a snippet could report findings that `cora scan` suppresses. It now goes through the shared post-processing pipeline as a synthetic `snippet.<language>` file (so path-aware classification applies), loads the project config like the other MCP tools (and fails with the same "Failed to load config" error), and mentions how many findings the cap dropped. Output format is unchanged.

- **Secret-detection edge cases (#625, #628, #629, #630).** A string literal inside a constructor wrapper is now a literal: Java `new String("..")`, `Cow::Owned`, `Box::from`, `Secret::new`, Python `str("..")` (plain calls such as `get_password()` stay quiet) (#630). A literal default in the default position of an env read is flagged in code files: `os.environ.get("X", "lit")`, `os.getenv`, `process.env.X || "lit"` / `?? "lit"`, Rust `env::var("X").unwrap_or("lit".into())` / `unwrap_or_else(|_| "lit".to_string())`, Ruby `ENV.fetch`, PHP `getenv() ?:`; the env var name and name-only reads stay quiet (#628). Shell `${VAR:-literal}` / `${VAR:=literal}` is flagged, while `${VAR}`, `${VAR:-}` and `${VAR:?msg}` stay quiet (#629). The unquoted-YAML rule no longer applies to source code (`secret: Secret1Type` in `.ts`/`.py`), YAML flow-style `{ password: hunter2hunter2 }` is flagged, unspaced `password:=os.Getenv("X")` is no longer matched, and SQL `PASSWORD "..."` with double quotes is detected (#625). All-alphabetic unquoted YAML values (`password: correcthorsebattery`) remain undetected, tracked in #635. The LLM secret cross-check uses the same classification.

- **`rules_engine.max_findings` is applied after suppression and truncation is reported (#624).** The cap (default 5) used to be applied inside each scanner, before `ignore.rules` / `cora-ignore`, so suppressed findings consumed slots and pushed real findings out, and the rest were dropped silently. Scanners now run uncapped and the cap is enforced once in post-processing on deterministic findings only (LLM issues are never capped), highest severity first. `cora scan` and `cora review` print `⚠ N more deterministic findings not shown (...)` on stderr when findings are cut, and the `cora scan` summary mentions it; stdout formats are unchanged. `max_findings: 0` or `null` now means unlimited (previously `0` hid every finding).

## [0.17.2] - 2026-10-09

### Fixed
Expand Down Expand Up @@ -986,7 +1008,8 @@ Benchmarked on the cora-code repository (1,864 symbols, 115 Rust files, x86_64):
- **Cross-platform** — Linux (x86_64, ARM64), macOS (Apple Silicon), Windows (x86_64)
- **MIT License** — fully open source

[Unreleased]: https://github.com/codecoradev/cora-code/compare/v0.17.2...develop
[Unreleased]: https://github.com/codecoradev/cora-code/compare/v0.18.0...develop
[0.18.0]: https://github.com/codecoradev/cora-code/compare/v0.17.2...v0.18.0
[0.17.2]: https://github.com/codecoradev/cora-code/compare/v0.17.1...v0.17.2
[0.17.1]: https://github.com/codecoradev/cora-code/compare/v0.17.0...v0.17.1
[0.17.0]: https://github.com/codecoradev/cora-code/compare/v0.16.1...v0.17.0
Expand Down
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "cora-code"
version = "0.17.2"
version = "0.18.0"
edition = "2024"
description = "CLI-first AI code review — BYOK, diff/scan/branch, pre-commit hooks"
license = "Apache-2.0"
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ cargo install --git https://github.com/codecoradev/cora-code

> Pin a version: `CORA_VERSION=v0.6.1 curl -fsSL ... | sh`

**Upgrading:** run `cora upgrade` (downloads the latest release, verifies its SHA-256 checksum, replaces the binary) or `cora upgrade --check` to just see if one is available. If you installed via `cargo install --path .`, re-run that instead.
**Upgrading:** run `cora upgrade` (downloads the latest release, verifies its SHA-256 checksum and, when `cosign` is installed, its Sigstore signature, replaces the binary) or `cora upgrade --check` to just see if one is available. If you installed via `cargo install --path .`, re-run that instead.

**Verify which `cora` you're running** — `which -a cora` will reveal stale copies from other channels:

Expand Down
9 changes: 9 additions & 0 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,15 @@ Security-related areas of the codebase:
- `src/hook/` — Pre-commit hook integration
- `src/index/` — File access and SQLite storage

## Release Artifact Signing

Release checksums are signed with cosign keyless (Sigstore, GitHub OIDC) by the
`release.yml` workflow, and `install.sh` / `cora upgrade` verify the signature
when `cosign` is installed (`CORA_REQUIRE_SIGNATURE=1` makes it mandatory).
Manual verification, the exact `cosign verify-blob` flags, and the scope and
limits of this guarantee are documented in
[docs/installation.md](docs/installation.md#verify-release-signatures).

## Threat Model: Adversarial Source-Code Comments (ALIBI)

LLM-based reviewers are vulnerable to adversarial comments in the code under
Expand Down
Loading
Loading