Skip to content

Add Rust dependency audit to Makefile#354

Open
leynos wants to merge 29 commits into
mainfrom
feat/cargo-audit-to-makefile
Open

Add Rust dependency audit to Makefile#354
leynos wants to merge 29 commits into
mainfrom
feat/cargo-audit-to-makefile

Conversation

@leynos
Copy link
Copy Markdown
Owner

@leynos leynos commented May 19, 2026

Summary

This branch extends the repository audit gate so Rust dependencies are checked alongside the existing frontend audit workflow. It splits the Makefile target into frontend and Rust phases, installs cargo-audit in CI before the audit gate, updates vulnerable dependency versions found while exercising the expanded target, updates the CI Rust setup action to the current leynos/shared-actions default-branch commit, and raises the ws override to 8.20.1 so GHSA-58qx-3vcg-4xpx no longer fails the frontend audit.

No roadmap task, issue, or execplan was identified for this branch.

Review walkthrough

  • Start with Makefile to see how make audit now delegates to audit-node and rust-audit, including the scoped RUSTSEC-2023-0071 ignore for SQLx's optional MySQL dependency path.
  • Then review .github/workflows/ci.yml for the CI installation of cargo-audit and the updated leynos/shared-actions setup-rust pin at e4c6b0e200a057edf927c45c298e7ddf229b3934.
  • Check backend/Cargo.toml, Cargo.lock, package.json, and pnpm-lock.yaml for the dependency updates that clear actionable audit violations, including the ws 8.20.1 override used by Puppeteer's puppeteer-core dependency.
  • Finish with docs/repository-structure.md for the updated operator-facing description of the audit target.

Validation

  • make audit: passed; cargo audit reports no unignored vulnerabilities, the frontend audit no longer reports GHSA-58qx-3vcg-4xpx, and the SQLx optional MySQL RSA advisory remains ignored with a documented rationale.
  • make check-fmt: passed after the ws override update.
  • make lint: passed after the ws override update.
  • make test: passed after the ws override update; Rust nextest reported 1195 passed and 4 skipped, followed by passing frontend and workspace tests.
  • pnpm run test: passed as a focused audit-tooling regression check.
  • Earlier branch validation also included make fmt: passed.

Notes

cargo audit still reports allowed warnings for unmaintained, unsound, or yanked crates in the dependency graph. This branch addresses the audit violations that cause the target to fail and records the remaining RSA advisory as a scoped ignore because the workspace only enables PostgreSQL support for SQLx.

Summary by Sourcery

Add Rust dependency auditing to the repository-wide audit gate and refresh dependencies to satisfy the expanded checks.

New Features:

  • Introduce a Rust dependency audit step that runs cargo audit via the consolidated make audit target.

Enhancements:

  • Split the audit Makefile target into separate frontend and Rust phases with a scoped ignore for a known SQLx MySQL advisory.
  • Update backend Diesel-related Rust dependencies to newer compatible versions.
  • Refresh frontend devDependencies and overrides to resolve security advisories reported by the expanded audit.
  • Clarify repository documentation to describe the combined frontend and Rust audit behaviour.

CI:

  • Install cargo-audit in the CI workflow and bump the shared Rust setup action to the latest pinned commit.

Copy link
Copy Markdown

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry @leynos, you have reached your weekly rate limit of 2500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 19, 2026

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 6b8b5ef7-7904-4328-b2bb-cdf2b8a64cb6

📥 Commits

Reviewing files that changed from the base of the PR and between af495b3 and 352b4a1.

📒 Files selected for processing (1)
  • scripts/security-audit-helpers.test.mjs

Rust Dependency Audit Integration — updated summary

This PR expands the repository-wide dependency-audit quality gate to include Rust (cargo-audit) alongside the existing frontend (pnpm) audit and delivers supporting CI, Makefile, docs, tests and audit-helper refactors.

High-level goals achieved

  • Add Rust dependency auditing to the repository audit pipeline and CI.
  • Ensure actionable audit violations are cleared or scoped/justified.
  • Improve test coverage and make audit tooling injectable and testable.
  • Address CI flakes caused by embedded PostgreSQL binary downloads.

Key changes

  • CI
    • Install cargo-audit (pinned to 0.22.1 via cargo binstall) in .github/workflows/ci.yml and bump shared Rust setup action to commit e4c6b0e200a0.
    • Add an Architecture lint step and pin POSTGRESQL_VERSION (=16.10.0) in the Rust test environment to avoid cache/embedded-postgres download issues.
  • Makefile
    • Split audit into audit-node and rust-audit; make audit depends on both.
    • Add CARGO ?= cargo and CARGO_AUDIT_IGNORES := --ignore RUSTSEC-2023-0071 (scoped ignore for SQLx optional MySQL path).
    • Tests added to assert Makefile audit dry-run behaviour.
    • Reviewer notes: remove duplicate .PHONY entry and prefer ensure_tool macro ($(call ensure_tool,cargo-audit)) over inline command guards.
  • Rust deps
    • Bump pg_embedded_setup_unpriv 0.5.0 → 0.5.1 (fixes env propagation to pg_worker).
    • Upgrade Diesel-related crates (diesel 2.2 → 2.3, diesel-async 0.5 → 0.8, diesel_migrations 2.2 → 2.3).
  • Frontend deps
    • Bump @mermaid-js/mermaid-cli, ajv, markdownlint-cli; add pnpm override: ws → 8.20.1 to address GHSA-58qx-3vcg-4xpx.
  • Security/audit helpers
    • New modules: security/audit-package-data.js, security/audit-reporting.js, security/audit-exception-policy.js.
    • Refactor security/audit-utils.js to accept an injected auditIo adapter (all CLI/process/env/timer/fetch operations use auditIo) and re-export reporting helpers.
    • validate-audit.js now uses assertNoExpired from audit-exception-policy.js.
    • reportUnexpectedAdvisories now accepts an injectable reportingIo (defaults to console.error).
  • Tests & coverage
    • Add/extend tests: scripts/makefile-audit.test.mjs, scripts/security-audit-helpers.test.mjs, scripts/security-audit-reporting.test.mjs, and frontend-pwa/scripts/audit-utils.test.mjs.
    • Tests cover JSON parsing, pnpm ls traversal, bulk advisory normalisation & deduplication, timeout/abort handling, signalling behaviour, allowlist expiry validation, and Makefile contract tests.
  • Rust code hygiene
    • Update #[expect(dead_code, reason = ...)] annotations to reference docs/backend-roadmap.md 3.5.1 (#360) for deferred cleanup traceability.

Validation performed

  • Local/CI validation reported:
    • make audit: passed (no unignored cargo-audit vulnerabilities; frontend audit no longer reports GHSA-58qx-3vcg-4xpx).
    • make check-fmt, make lint, make test (Rust nextest: 1195 passed, 4 skipped), pnpm run test, make fmt: passed.
    • cargo audit: allowed warnings for unmaintained/unsound/yanked crates; RUSTSEC-2023-0071 scoped ignore recorded.
  • CI/debug notes:
    • Some CI backend tests previously timed out and one BDD test panicked due to pg-embed setup failing to decode GitHub API responses when GITHUB_TOKEN wasn’t propagated. Bumping pg_embedded_setup_unpriv to 0.5.1 and pinning POSTGRESQL_VERSION addressed the issue.

Reviewer observations & recommended follow-ups (actionable)

  • Makefile: remove duplicate .PHONY declaration and replace inline cargo-audit guard with $(call ensure_tool,cargo-audit); keep an install hint comment.
  • Bump pg-embed-setup-unpriv to 0.5.1 in backend/Cargo.toml and update Cargo.lock (already included in this PR).
  • Pin POSTGRESQL_VERSION in CI (done in this PR; keep pin to avoid repeat flakiness).
  • JavaScript complexity: several functions exceed cyclomatic complexity thresholds — suggested refactors by extracting small helpers (prompts included in PR for automated/AI-assisted refactors).
  • Add injectable IO adapter usage consistently (reportUnexpectedAdvisories already accepts reportingIo; prefer same pattern across modules).
  • Add end-to-end tests for validate-audit.js and the whole audit workflow; add tests for fetchBulkAdvisories timeout/abort handling (property tests included).
  • Track and remove #[expect(dead_code)] suppressions when roadmap items land — issue #360 opened.

Documentation

  • docs/repository-structure.md and docs/developers-guide.md updated to document combined frontend + Rust audit behaviour, Corepack/pnpm expectations, exception allowlist policy, and audit helper responsibilities.

Notes on traceability

  • No new execplan document was added; changes reference existing docs (developers guide, repository-structure, backend-roadmap) per commit annotations. Issue #360 was opened to track deferred Rust cleanup.

Walkthrough

Split repository audits into audit-node and rust-audit, install cargo-audit in CI, add an architecture lint step, parameterise audit I/O and reporting helpers, add Makefile/audit tests, bump select dependencies, and standardise backend expect annotations.

Changes

Unified Audit and Dependency Maintenance

Layer / File(s) Summary
Audit infrastructure in Makefile
Makefile
CARGO and CARGO_AUDIT_IGNORES added; extend .PHONY; split audit into audit-node (workspace pnpm audits + pnpm run audit:validate) and rust-audit (check cargo-audit, run cargo audit --file Cargo.lock with ignore).
CI cargo-audit setup & lint step
.github/workflows/ci.yml
Update pinned setup-rust action commit; add Install cargo-audit step via cargo binstall --no-confirm for cargo-audit@0.22.1; add make lint-architecture step; pin POSTGRESQL_VERSION to =16.10.0.
Docs: repository & developer guides
docs/repository-structure.md, docs/developers-guide.md
Document make audit flow and security/ helpers, require Corepack for pnpm@10.15.1, describe pnpm→npm fallback, and add make audit to quality gates and local setup instructions.
Dependency version updates
backend/Cargo.toml, package.json
Bump pg_embedded_setup_unpriv, diesel/diesel-async/diesel_migrations, and frontend devDependencies/pnpm.overrides (@mermaid-js/mermaid-cli, ajv, markdownlint-cli, qs).
Makefile audit contract tests
scripts/makefile-audit.test.mjs
Add Vitest dry-run tests asserting make audit includes both Node and Rust audit steps, checks rust-audit cargo-audit availability/pinned version, and verifies cargo audit invocation with configured ignore; add execution test using temporary shims.
Frontend audit tests adjustments
frontend-pwa/scripts/audit-utils.test.mjs
Model pnpm process termination via nullable signal and status; add tests asserting runAuditJson() rejects on signalled pnpm commands and that bulk advisory normalisation omits github_advisory_id when absent.
Audit package-data helpers
security/audit-package-data.js
Add parseJsonOutput, buildVersionMap, loadPackageTrees, collectInstalledPackageVersions, and normalizeBulkAdvisories for pnpm tree parsing, version collection, GHSA extraction, deduplication and validation.
Audit IO abstraction & utils
security/audit-utils.js
Introduce defaultAuditIo, add assertCompletedProcess, thread auditIo through readRegistryUrl, fetchBulkAdvisories, runBulkAdvisoryAudit, and change exported runAuditJson(auditIo = defaultAuditIo) to use injected IO and preserve fallback behaviour; re-export package-data helpers.
Audit reporting helpers
security/audit-reporting.js
Add partitionAdvisoriesById(advisories, allowedIds) and reportUnexpectedAdvisories(unexpected, heading, reportingIo = defaultReportingIo) to partition advisories and report unexpected ones to stderr.
Audit exception policy & validate import
security/audit-exception-policy.js, security/validate-audit.js
Add assertNoExpired(entries, currentDate = new Date(), policyIo) and import it into validate-audit to centralise expiry and invalid-range checks.
Backend expect attributes and test struct
backend/src/..., backend/tests/...
Standardise #[expect(dead_code, reason = ...)] messages to reference docs/backend-roadmap.md 3.5.1; reformat DatabaseHandle tuple struct to attach the expect attribute explicitly; no API or schema changes.

Sequence Diagram(s)

sequenceDiagram
  participant Makefile
  participant PNPM as pnpm
  participant CI as CI
  participant CargoBinstall as cargo-binstall
  participant CargoAudit as cargo-audit
  participant AuditUtils as security/audit-utils.js
  participant PackageData as security/audit-package-data.js
  participant Registry as npm-registry

  Makefile->>PNPM: run workspace audits (pnpm -r --if-present run audit)
  Makefile->>PNPM: run pnpm run audit:validate
  CI->>CargoBinstall: run cargo binstall --no-confirm cargo-audit@0.22.1
  CargoBinstall->>CargoAudit: provide cargo-audit binary
  Makefile->>CargoAudit: invoke cargo audit --file Cargo.lock --ignore RUSTSEC-2023-0071
  PNPM-->>AuditUtils: stdout/json or retirement error
  AuditUtils->>PackageData: collectInstalledPackageVersions(auditIo)
  AuditUtils->>Registry: POST bulk advisory query with Abort (fallback)
  Registry-->>AuditUtils: bulk advisory payload
  AuditUtils->>PackageData: normalizeBulkAdvisories(payload)
Loading

Possibly related issues

Possibly related PRs

  • leynos/corbusier#79 — Similar Makefile/CI audit restructuring and cargo-audit installation workflow.

Suggested reviewers

  • codescene-delta-analysis

Poem

🦀 Install the bins, run audits both near and far,
Split Node and Rust, pin the versions where they are.
Map packages, normalise advisories in kind,
Report the unexpected, keep allowlists aligned.
Tests assert the plan; docs record the guard.


Caution

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

  • Ignore

❌ Failed checks (1 error, 5 warnings)

Check name Status Explanation Resolution
Testing (Overall) ❌ Error validate-audit.js is untested; reportUnexpectedAdvisories empty-input test is vacuous (checks only mocks); Makefile tests use weak string matching rather than structural assertions. Add integration tests for validate-audit.js workflow; strengthen reportUnexpectedAdvisories tests; add ordering assertions to Makefile tests for cargo-audit check before invocation.
Developer Documentation ⚠️ Warning Docs for reportUnexpectedAdvisories omit the reportingIo adapter parameter. Signature shows (unexpected, heading) but code has (unexpected, heading, reportingIo = defaultReportingIo). Add reportingIo parameter to reportUnexpectedAdvisories documentation in developers-guide.md to match actual signature and existing adapter pattern.
Testing (Unit And Behavioural) ⚠️ Warning Unit tests adequately cover local behaviour and edge cases. End-to-end tests for validate-audit.js script and the audit workflow observable behaviour are missing. Add E2E tests for validate-audit.js covering schema validation, expiry checks, and advisory mismatches. Add workflow tests verifying audit command exit codes and error outputs.
Testing (Property / Proof) ⚠️ Warning Property tests are included for deduplication/partitioning but missing for parseJsonOutput, loadPackageTrees, and collectInstalledPackageVersions which introduce parsing and traversal invariants. Add fast-check properties for: parseJsonOutput with fc.anyJson(), loadPackageTrees with variable tree structures, and collectInstalledPackageVersions with reordered inputs to verify order-independence.
Observability ⚠️ Warning Audit operations lack structured logging, metrics, tracing, and correlation IDs for production diagnostics; cargo audit output not captured. Add structured logging with operation names; emit latency and timeout metrics; add correlation IDs to bulk-advisory requests; capture and report cargo audit execution details.
Architectural Complexity And Maintainability ⚠️ Warning Four new modules add auditIo adapter injection lacking clear justification. Tests use inline mocks, not vi.mock() patterns used elsewhere. No documented reuse path exists. Justify auditIo injection over vi.mock(). Document reuse plans for new modules or consolidate into audit-utils to reduce abstraction.
✅ Passed checks (14 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately reflects the main change: adding Rust dependency auditing capability via the Makefile. It directly corresponds to the primary objective of the PR.
Description check ✅ Passed The description comprehensively details the changes: Makefile audit target split, CI cargo-audit installation, dependency updates, and documentation updates. It closely aligns with the changeset across all modified files.
Docstring Coverage ✅ Passed Docstring coverage is 86.96% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
User-Facing Documentation ✅ Passed PR contains only internal developer tooling, CI configuration, and dependency maintenance—no user-facing functionality or behaviour changes. Check not applicable.
Module-Level Documentation ✅ Passed All new and modified modules carry @file JSDoc docstrings explaining purpose, utility, and component relationships. Comprehensive modules detail dependencies and callers.
Testing (Compile-Time / Ui) ✅ Passed PR includes appropriate inline snapshots for text-based audit output and focused semantic assertions for Makefile audit contracts, with no compile-time test requirements.
Unit Architecture ✅ Passed Query functions perform no mutations; commands use explicit IO injection; fallibility explicit in APIs; boundaries isolated via adapters; single responsibility per module.
Domain Architecture ✅ Passed Audit domain logic properly separated via injectable adapters (auditIo, reportingIo, policyIo); policies and reporting cleanly abstracted; infrastructure helpers acceptable at build-time layer.
Security And Privacy ✅ Passed No secrets exposed. Input validation comprehensive. GITHUB_TOKEN properly isolated in CI. Error handling safe. Test credentials test-only. Dependencies secure.
Performance And Resource Use ✅ Passed Resource use is bounded: 64 MiB buffers, O(N) tree walking, O(M) advisory normalisation, single-pass I/O with 30s timeout, no loops with repeated I/O.
Concurrency And State ✅ Passed No concurrency issues. Timeouts use try/finally cleanup; AbortController cancels fetch and response reads; state via injected auditIo adapter; CI sequential; property tests verify both scenarios.
Rust Compiler Lint Integrity ✅ Passed PR uses #[expect(dead_code)] with tracking refs (roadmap 3.5.1, 2.4.3, issue #360) for in-progress work, not #[allow]. Compiler feedback integrity preserved.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/cargo-audit-to-makefile

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint skipped: no ESLint configuration detected in root package.json. To enable, add eslint to devDependencies.


Comment @coderabbitai help to get the list of available commands and usage tips.

codescene-delta-analysis[bot]

This comment was marked as outdated.

codescene-delta-analysis[bot]

This comment was marked as outdated.

codescene-delta-analysis[bot]

This comment was marked as outdated.

leynos added 3 commits May 20, 2026 21:57
Split the audit target into frontend and Rust phases, and run
`cargo audit` against the workspace lockfile. Install `cargo-audit`
in CI before the audit gate so the target is available on runners.

Update vulnerable frontend and Rust dependencies to patched releases.
Keep the SQLx optional MySQL RSA advisory ignored because this
workspace only enables PostgreSQL and RustSec has no fixed RSA release.
Point the CI Rust setup action at the current `leynos/shared-actions`
default-branch commit so the workflow uses the latest shared action.
Restore the `openapi-dump` and `er-snapshots` binary entries that were
collapsed into duplicate `ingest-osm` targets during conflict resolution.
Keep the rebased manifest aligned with `origin/main` while preserving the
audit branch changes.
@lodyai lodyai Bot force-pushed the feat/cargo-audit-to-makefile branch from beeeb98 to 1a63868 Compare May 20, 2026 20:06
codescene-delta-analysis[bot]

This comment was marked as outdated.

@leynos leynos marked this pull request as ready for review May 21, 2026 20:20
@sourcery-ai
Copy link
Copy Markdown

sourcery-ai Bot commented May 21, 2026

Reviewer's Guide

Adds a Rust dependency audit phase to the repository’s audit gate, wires it into CI, and updates Rust and frontend dependency versions to clear actionable vulnerabilities while documenting the new behavior.

Flow diagram for updated make audit target with Rust dependency auditing

flowchart LR
    actor_CI[CI or developer]
    actor_CI --> make_audit[make audit]

    make_audit --> audit_node[audit-node: pnpm -r install && pnpm -r run audit && pnpm run audit:validate]
    make_audit --> rust_audit[rust-audit: cargo audit --file Cargo.lock --ignore RUSTSEC-2023-0071]

    audit_node --> frontend_deps[Frontend dependencies audited]
    rust_audit --> rust_deps[Rust dependencies audited]
Loading

File-Level Changes

Change Details Files
Split the audit Makefile target into separate frontend and Rust phases and configure cargo-audit behavior.
  • Introduce CARGO variable and CARGO_AUDIT_IGNORES flag in the Makefile and extend PATH for cargo binaries.
  • Mark audit, audit-node, and rust-audit as PHONY targets and make audit depend on audit-node and rust-audit.
  • Retain the existing pnpm-based frontend audit under audit-node and add a rust-audit target that runs cargo audit against Cargo.lock with a scoped ignore for RUSTSEC-2023-0071.
Makefile
Document the updated audit workflow for operators.
  • Update repository-structure documentation to describe make audit as running both frontend and Rust audits.
  • Clarify that the frontend audit validates the exception allowlist and that the Rust audit runs cargo audit on the root Cargo.lock while preserving Corepack guidance.
docs/repository-structure.md
Refresh Node dev tooling dependencies and overrides to satisfy the updated audit gate.
  • Bump @mermaid-js/mermaid-cli, ajv, and markdownlint-cli devDependency versions.
  • Align the ajv override to the new version and keep other security-related overrides (fast-uri, glob, js-yaml, lodash, ws via lockfile) intact.
package.json
pnpm-lock.yaml
Update Rust backend dependencies to non-vulnerable versions.
  • Bump diesel, diesel-async, and diesel_migrations to newer minor versions compatible with the existing feature flags.
  • Regenerate Cargo.lock to reflect updated Rust dependency graph and resolve audit violations.
backend/Cargo.toml
Cargo.lock
Extend CI to support Rust auditing and align with the shared Rust setup action pin.
  • Update the leynos/shared-actions setup-rust action reference to a newer commit.
  • Install cargo-audit in the CI job via cargo binstall before running the audit stage so make audit exercises both frontend and Rust audits.
.github/workflows/ci.yml

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

sourcery-ai[bot]

This comment was marked as resolved.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/ci.yml:
- Around line 94-95: Update the "Install cargo-audit" CI step so it installs a
fixed release instead of the latest; replace the current command string "cargo
binstall --no-confirm cargo-audit" with one that pins the package to version
0.22.1 (e.g., using the cargo-binstall package@version form) so the CI
consistently installs cargo-audit@0.22.1.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 379d987d-38f8-44c5-b9ed-2fb95f4fd2c4

📥 Commits

Reviewing files that changed from the base of the PR and between b23476f and 1a63868.

⛔ Files ignored due to path filters (2)
  • Cargo.lock is excluded by !**/*.lock
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (5)
  • .github/workflows/ci.yml
  • Makefile
  • backend/Cargo.toml
  • docs/repository-structure.md
  • package.json

Comment thread .github/workflows/ci.yml Outdated
leynos added 2 commits May 23, 2026 14:06
Add `make audit` to the documented quality gates and note that the
local setup expects Corepack to enable `pnpm`. This keeps the developer
guide aligned with the repository-structure guidance.
Remove the redundant node audit install, add an explicit `cargo-audit`
availability check, and pin the CI install to `cargo-audit@0.22.1`.

Thread audit I/O and expiry-date dependencies through the audit helpers so
fallible process and network work is explicit at the boundary. Add required
`#[expect]` reasons and roadmap links for still-valid lint suppressions.

Update the `qs` override to `6.15.2` and refresh the pnpm lockfile to clear
the current `GHSA-q8mj-m7cp-5q26` audit violation.
codescene-delta-analysis[bot]

This comment was marked as outdated.

@leynos
Copy link
Copy Markdown
Owner Author

leynos commented May 23, 2026

@coderabbitai Please investigate the cause of the following issue using codegraph exploration and research, identify a fix and provide an AI coding agent prompt for the fix:

        PASS [   0.006s] ( 675/1105) backend::diesel_login_users_adapters support::embedded_postgres::tests::setup_apalis_storage_is_linked
 TERMINATING [> 60.000s] (─────────) backend::diesel_login_users_adapters db_present_mode_supports_login_and_users_with_stable_contracts
 TERMINATING [> 60.000s] (─────────) backend::diesel_login_users_adapters db_present_mode_handles_users_table_loss_with_stable_outcomes
     TIMEOUT [  60.003s] ( 676/1105) backend::diesel_login_users_adapters db_present_mode_supports_login_and_users_with_stable_contracts
  stdout ───

    running 1 test

    (test timed out)

  Cancelling due to test failure: 3 tests still running
     TIMEOUT [  60.003s] ( 677/1105) backend::diesel_login_users_adapters db_present_mode_handles_users_table_loss_with_stable_outcomes
  stdout ───

    running 1 test

    (test timed out)

 TERMINATING [> 60.000s] (─────────) backend::diesel_login_users_adapters startup_modes_reject_invalid_credentials_with_unauthorised_envelope::case_2
     TIMEOUT [  60.002s] ( 678/1105) backend::diesel_login_users_adapters startup_modes_reject_invalid_credentials_with_unauthorised_envelope::case_2
  stdout ───

    running 1 test

    (test timed out)

        FAIL [ 169.168s] ( 679/1105) backend::catalogue_descriptor_ingestion_bdd catalogue_and_descriptor_ingestion_supports_success_failure_and_nullable_edge_cases
  stdout ───

    running 1 test
    test catalogue_and_descriptor_ingestion_supports_success_failure_and_nullable_edge_cases has been running for over 60 seconds
    test catalogue_and_descriptor_ingestion_supports_success_failure_and_nullable_edge_cases ... FAILED

    failures:

    failures:
        catalogue_and_descriptor_ingestion_supports_success_failure_and_nullable_edge_cases

    test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 8 filtered out; finished in 169.16s
    
  stderr ───
    The application panicked (crashed).
    Message:  Test cluster setup failed: shared cluster initialisation previously failed: BootstrapError { kind: Other, report: 
       0: bootstrap failed: BootstrapError { kind: Other, report: 
             0: postgresql_embedded::setup() failed
             1: error decoding response body

          Location:
             /home/runner/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/pg-embed-setup-unpriv-0.5.0/src/cluster/worker_invoker/mod.rs:324

            ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ BACKTRACE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
             1: <color_eyre[8a036c914ad27c05]::config::EyreHook>::into_eyre_hook::{closure#0}<unknown>
                at <unknown source file>:<unknown line>
             2: eyre[ade532eb8244d9bf]::capture_handler<unknown>
                at <unknown source file>:<unknown line>
             3: <eyre[ade532eb8244d9bf]::Report>::from_msg::<&str, postgresql_embedded[b4d2603bee94b72]::error::Error><unknown>
                at <unknown source file>:<unknown line>
             4: <pg_embedded_setup_unpriv[300f00fc572c2aec]::cluster::worker_invoker::WorkerInvoker>::invoke::<pg_embedded_setup_unpriv[300f00fc572c2aec]::cluster::startup::invoke_lifecycle::{closure#0}><unknown>
                at <unknown source file>:<unknown line>
             5: <pg_embedded_setup_unpriv[300f00fc572c2aec]::cluster::TestCluster>::new_split<unknown>
                at <unknown source file>:<unknown line>
             6: pg_embedded_setup_unpriv[300f00fc572c2aec]::test_support::shared_singleton::shared_cluster_handle<unknown>
                at <unknown source file>:<unknown line>
             7: catalogue_descriptor_ingestion_bdd[d2d6c55bdc757b91]::support::atexit_cleanup::shared_cluster_handle<unknown>
                at <unknown source file>:<unknown line>
             8: <catalogue_descriptor_ingestion_bdd[d2d6c55bdc757b91]::catalogue_and_descriptor_ingestion_supports_success_failure_and_nullable_edge_cases::{closure#0} as core[274c285622937fcf]::ops::function::FnOnce<()>>::call_once<unknown>
                at <unknown source file>:<unknown line>
             9: <fn() -> core[274c285622937fcf]::result::Result<(), alloc[58d6849b0d6b715d]::string::String> as core[274c285622937fcf]::ops::function::FnOnce<()>>::call_once<unknown>
                at /rustc/54333ff079780f803f65dcee30c544050b35f544/library/core/src/ops/function.rs:250
            10: test[22843e112f4c7c90]::__rust_begin_short_backtrace::<core[274c285622937fcf]::result::Result<(), alloc[58d6849b0d6b715d]::string::String>, fn() -> core[274c285622937fcf]::result::Result<(), alloc[58d6849b0d6b715d]::string::String>><unknown>
                at /rustc/54333ff079780f803f65dcee30c544050b35f544/library/test/src/lib.rs:724
            11: test[22843e112f4c7c90]::run_test_in_process::{closure#0}<unknown>
          at <unknown source file>:<unknown line>
       2: <color_eyre[8a036c914ad27c05]::config::PanicHook>::into_panic_hook::{closure#0}<unknown>
          at <unknown source file>:<unknown line>
       3: <alloc[58d6849b0d6b715d]::boxed::Box<dyn for<'a, 'b> core[274c285622937fcf]::ops::function::Fn<(&'a std[123cabe7bf2a605c]::panic::PanicHookInfo<'b>,), Output = ()> + core[274c285622937fcf]::marker::Sync + core[274c285622937fcf]::marker::Send> as core[274c285622937fcf]::ops::function::Fn<(&std[123cabe7bf2a605c]::panic::PanicHookInfo,)>>::call<unknown>
          at /rustc/54333ff079780f803f65dcee30c544050b35f544/library/alloc/src/boxed.rs:2289
       4: std[123cabe7bf2a605c]::panicking::panic_with_hook<unknown>
          at /rustc/54333ff079780f803f65dcee30c544050b35f544/library/std/src/panicking.rs:833
       5: std[123cabe7bf2a605c]::panicking::panic_handler::{closure#0}<unknown>
          at /rustc/54333ff079780f803f65dcee30c544050b35f544/library/std/src/panicking.rs:698
       6: std[123cabe7bf2a605c]::sys::backtrace::__rust_end_short_backtrace::<std[123cabe7bf2a605c]::panicking::panic_handler::{closure#0}, !><unknown>
          at /rustc/54333ff079780f803f65dcee30c544050b35f544/library/std/src/sys/backtrace.rs:182
       7: __rustc[27fdca0b0050a95b]::rust_begin_unwind<unknown>
          at /rustc/54333ff079780f803f65dcee30c544050b35f544/library/std/src/panicking.rs:689
       8: core[274c285622937fcf]::panicking::panic_fmt<unknown>
          at /rustc/54333ff079780f803f65dcee30c544050b35f544/library/core/src/panicking.rs:80
       9: catalogue_descriptor_ingestion_bdd[d2d6c55bdc757b91]::support::cluster_skip::handle_cluster_setup_failure::<(), &alloc[58d6849b0d6b715d]::string::String><unknown>
          at <unknown source file>:<unknown line>
      10: <catalogue_descriptor_ingestion_bdd[d2d6c55bdc757b91]::catalogue_and_descriptor_ingestion_supports_success_failure_and_nullable_edge_cases::{closure#0} as core[274c285622937fcf]::ops::function::FnOnce<()>>::call_once<unknown>
          at <unknown source file>:<unknown line>
      11: <fn() -> core[274c285622937fcf]::result::Result<(), alloc[58d6849b0d6b715d]::string::String> as core[274c285622937fcf]::ops::function::FnOnce<()>>::call_once<unknown>
          at /rustc/54333ff079780f803f65dcee30c544050b35f544/library/core/src/ops/function.rs:250
      12: test[22843e112f4c7c90]::__rust_begin_short_backtrace::<core[274c285622937fcf]::result::Result<(), alloc[58d6849b0d6b715d]::string::String>, fn() -> core[274c285622937fcf]::result::Result<(), alloc[58d6849b0d6b715d]::string::String>><unknown>
          at /rustc/54333ff079780f803f65dcee30c544050b35f544/library/test/src/lib.rs:724
      13: test[22843e112f4c7c90]::run_test_in_process::{closure#0}<unknown>
          at /rustc/54333ff079780f803f65dcee30c544050b35f544/library/test/src/lib.rs:747
      14: <core[274c285622937fcf]::panic::unwind_safe::AssertUnwindSafe<test[22843e112f4c7c90]::run_test_in_process::{closure#0}> as core[274c285622937fcf]::ops::function::FnOnce<()>>::call_once<unknown>
          at /rustc/54333ff079780f803f65dcee30c544050b35f544/library/core/src/panic/unwind_safe.rs:275
      15: std[123cabe7bf2a605c]::panicking::catch_unwind::do_call::<core[274c285622937fcf]::panic::unwind_safe::AssertUnwindSafe<test[22843e112f4c7c90]::run_test_in_process::{closure#0}>, core[274c285622937fcf]::result::Result<(), alloc[58d6849b0d6b715d]::string::String>><unknown>
          at /rustc/54333ff079780f803f65dcee30c544050b35f544/library/std/src/panicking.rs:581
      16: std[123cabe7bf2a605c]::panicking::catch_unwind::<core[274c285622937fcf]::result::Result<(), alloc[58d6849b0d6b715d]::string::String>, core[274c285622937fcf]::panic::unwind_safe::AssertUnwindSafe<test[22843e112f4c7c90]::run_test_in_process::{closure#0}>><unknown>
          at /rustc/54333ff079780f803f65dcee30c544050b35f544/library/std/src/panicking.rs:544
      17: std[123cabe7bf2a605c]::panic::catch_unwind::<core[274c285622937fcf]::panic::unwind_safe::AssertUnwindSafe<test[22843e112f4c7c90]::run_test_in_process::{closure#0}>, core[274c285622937fcf]::result::Result<(), alloc[58d6849b0d6b715d]::string::String>><unknown>
          at /rustc/54333ff079780f803f65dcee30c544050b35f544/library/std/src/panic.rs:359
      18: test[22843e112f4c7c90]::run_test_in_process<unknown>
          at /rustc/54333ff079780f803f65dcee30c544050b35f544/library/test/src/lib.rs:747
      19: test[22843e112f4c7c90]::run_test::{closure#0}<unknown>
          at /rustc/54333ff079780f803f65dcee30c544050b35f544/library/test/src/lib.rs:668
      20: test[22843e112f4c7c90]::run_test::{closure#1}<unknown>
          at /rustc/54333ff079780f803f65dcee30c544050b35f544/library/test/src/lib.rs:698
      21: std[123cabe7bf2a605c]::sys::backtrace::__rust_begin_short_backtrace::<test[22843e112f4c7c90]::run_test::{closure#1}, ()><unknown>
          at /rustc/54333ff079780f803f65dcee30c544050b35f544/library/std/src/sys/backtrace.rs:166
      22: std[123cabe7bf2a605c]::thread::lifecycle::spawn_unchecked::<test[22843e112f4c7c90]::run_test::{closure#1}, ()>::{closure#1}::{closure#0}<unknown>
          at /rustc/54333ff079780f803f65dcee30c544050b35f544/library/std/src/thread/lifecycle.rs:70
      23: <core[274c285622937fcf]::panic::unwind_safe::AssertUnwindSafe<std[123cabe7bf2a605c]::thread::lifecycle::spawn_unchecked<test[22843e112f4c7c90]::run_test::{closure#1}, ()>::{closure#1}::{closure#0}> as core[274c285622937fcf]::ops::function::FnOnce<()>>::call_once<unknown>
          at /rustc/54333ff079780f803f65dcee30c544050b35f544/library/core/src/panic/unwind_safe.rs:275
      24: std[123cabe7bf2a605c]::panicking::catch_unwind::do_call::<core[274c285622937fcf]::panic::unwind_safe::AssertUnwindSafe<std[123cabe7bf2a605c]::thread::lifecycle::spawn_unchecked<test[22843e112f4c7c90]::run_test::{closure#1}, ()>::{closure#1}::{closure#0}>, ()><unknown>
          at /rustc/54333ff079780f803f65dcee30c544050b35f544/library/std/src/panicking.rs:581
      25: std[123cabe7bf2a605c]::panicking::catch_unwind::<(), core[274c285622937fcf]::panic::unwind_safe::AssertUnwindSafe<std[123cabe7bf2a605c]::thread::lifecycle::spawn_unchecked<test[22843e112f4c7c90]::run_test::{closure#1}, ()>::{closure#1}::{closure#0}>><unknown>
          at /rustc/54333ff079780f803f65dcee30c544050b35f544/library/std/src/panicking.rs:544
      26: std[123cabe7bf2a605c]::panic::catch_unwind::<core[274c285622937fcf]::panic::unwind_safe::AssertUnwindSafe<std[123cabe7bf2a605c]::thread::lifecycle::spawn_unchecked<test[22843e112f4c7c90]::run_test::{closure#1}, ()>::{closure#1}::{closure#0}>, ()><unknown>
          at /rustc/54333ff079780f803f65dcee30c544050b35f544/library/std/src/panic.rs:359
      27: std[123cabe7bf2a605c]::thread::lifecycle::spawn_unchecked::<test[22843e112f4c7c90]::run_test::{closure#1}, ()>::{closure#1}<unknown>
          at /rustc/54333ff079780f803f65dcee30c544050b35f544/library/std/src/thread/lifecycle.rs:68
      28: <std[123cabe7bf2a605c]::thread::lifecycle::spawn_unchecked<test[22843e112f4c7c90]::run_test::{closure#1}, ()>::{closure#1} as core[274c285622937fcf]::ops::function::FnOnce<()>>::call_once::{shim:vtable#0}<unknown>
          at /rustc/54333ff079780f803f65dcee30c544050b35f544/library/core/src/ops/function.rs:250
      29: <alloc[58d6849b0d6b715d]::boxed::Box<dyn core[274c285622937fcf]::ops::function::FnOnce<(), Output = ()> + core[274c285622937fcf]::marker::Send> as core[274c285622937fcf]::ops::function::FnOnce<()>>::call_once<unknown>
          at /rustc/54333ff079780f803f65dcee30c544050b35f544/library/alloc/src/boxed.rs:2275
      30: <std[123cabe7bf2a605c]::sys::thread::unix::Thread>::new::thread_start<unknown>
          at /rustc/54333ff079780f803f65dcee30c544050b35f544/library/std/src/sys/thread/unix.rs:118

    Run with COLORBT_SHOW_HIDDEN=1 environment variable to disable frame filtering.
    Run with RUST_BACKTRACE=full to include source snippets.

────────────
     Summary [ 169.538s] 679/1105 tests run: 675 passed, 1 failed, 3 timed out, 4 skipped
     TIMEOUT [  60.003s] ( 676/1105) backend::diesel_login_users_adapters db_present_mode_supports_login_and_users_with_stable_contracts
     TIMEOUT [  60.003s] ( 677/1105) backend::diesel_login_users_adapters db_present_mode_handles_users_table_loss_with_stable_outcomes
     TIMEOUT [  60.002s] ( 678/1105) backend::diesel_login_users_adapters startup_modes_reject_invalid_credentials_with_unauthorised_envelope::case_2
        FAIL [ 169.168s] ( 679/1105) backend::catalogue_descriptor_ingestion_bdd catalogue_and_descriptor_ingestion_supports_success_failure_and_nullable_edge_cases
warning: 426/1105 tests were not run due to test failure (run with --no-fail-fast to run all tests, or run with --max-fail)

Document that `make audit` covers both frontend and Rust dependency
checks, and note the `cargo-audit` prerequisite alongside Corepack so the
local setup matches the Makefile and CI flow.
@coderabbitai

This comment was marked as resolved.

Add root Vitest coverage for the `rust-audit` target, including the
`cargo-audit` availability guard, command shape, and configured RustSec
ignore.

Run architecture linting in CI and tighten the remaining lint expectation
reasons with concrete roadmap references.
codescene-delta-analysis[bot]

This comment was marked as outdated.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
security/audit-utils.js (1)

1-416: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Decompose this module to comply with the 400-line limit.

The file is 416 lines, exceeding the 400-line maximum. Extract one of the following into a dedicated module:

  • Advisory normalisation helpers (deriveAdvisoryKey, normalizeBulkAdvisories, addPackageAdvisories, lines 195–265).
  • Reporting utilities (formatAdvisoryLine, reportUnexpectedAdvisories, partitionAdvisoriesById, lines 362–415).

As per coding guidelines: "No single code file may be longer than 400 lines; long switch statements or dispatch tables should be broken up by feature, and large test data blocks moved to external files."

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/developers-guide.md`:
- Line 26: The repository-wide commit-gates list in the developers guide is
missing `make audit`; update the "commit-gates" list (the section that lists
required commands for commits) to include `make audit` alongside the existing
entries so the guide and other quality-gate lists are synchronized; ensure the
entry is added in the same format as the other commands in that list.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 78fadc3e-0258-4fd5-b9a3-e0f430f775de

📥 Commits

Reviewing files that changed from the base of the PR and between 1a63868 and 3cf72f3.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (10)
  • .github/workflows/ci.yml
  • Makefile
  • backend/src/inbound/http/schemas.rs
  • backend/src/outbound/persistence/models.rs
  • backend/tests/example_data_runs_bdd.rs
  • docs/developers-guide.md
  • package.json
  • scripts/makefile-audit.test.mjs
  • security/audit-utils.js
  • security/validate-audit.js

Comment thread docs/developers-guide.md
@leynos
Copy link
Copy Markdown
Owner Author

leynos commented May 24, 2026

@coderabbitai Have the following now been resolved?

  • In the Makefile, audit audit-node rust-audit are declared as .PHONY twice; you can drop the second .PHONY line to avoid redundancy.
  • Consider using the existing ensure_tool helper (or similar check) for cargo-audit in the rust-audit target so local runs fail with a clear message if the tool is missing, instead of relying solely on CI installation.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 24, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Read the full Makefile to check .PHONY declarations and rust-audit target
cat Makefile

Length of output: 8603


@leynos — neither issue has been resolved yet.

1. Duplicate .PHONY declaration — the consolidated declaration near the top of the file already contains audit audit-node rust-audit, but the standalone line immediately below it remains:

.PHONY: all clean be fe … audit audit-node rust-audit \
        lint-rust … workspace-sync
.PHONY: audit audit-node rust-audit   ← remove this line

2. ensure_tool not used in rust-audit — the target rolls its own inline check instead of calling the ensure_tool macro that is already defined and used elsewhere in the file:

# Current (inline check)
rust-audit:
	`@command` -v cargo-audit >/dev/null 2>&1 || { echo "Error: cargo-audit is required. Install it with 'cargo binstall --no-confirm cargo-audit@0.22.1'."; exit 1; }
	$(CARGO) audit --file Cargo.lock $(CARGO_AUDIT_IGNORES)

# Fix: use the existing macro, keep the install hint in a comment
rust-audit:
	$(call ensure_tool,cargo-audit)
	# RUSTSEC-2023-0071 is in SQLx's optional MySQL support; this workspace only enables PostgreSQL.
	# Install cargo-audit with: cargo binstall --no-confirm cargo-audit@0.22.1
	$(CARGO) audit --file Cargo.lock $(CARGO_AUDIT_IGNORES)

Remove the duplicate .PHONY line and replace the inline guard with $(call ensure_tool,cargo-audit).

Bump `pg-embed-setup-unpriv` to `0.5.1` and refresh `Cargo.lock`
so the worker subprocess receives the parent environment in CI. Pin the
embedded PostgreSQL runtime version in the Rust test environment to avoid
release-listing requests during archive resolution.

Extract audit reporting helpers from `security/audit-utils.js` so the
shared audit utility module stays below the repository file-size limit.
codescene-delta-analysis[bot]

This comment was marked as outdated.

@leynos
Copy link
Copy Markdown
Owner Author

leynos commented May 24, 2026

@coderabbitai Please suggest a fix for this issue and supply a prompt for an AI coding agent to enable it to apply the fix. Include the file and symbol names indicated in the issue at the head of your response.

security/audit-utils.js

Comment on file

import { execFileSync, spawnSync } from 'node:child_process';

export {

❌ New issue: Overall Code Complexity
This module has a mean cyclomatic complexity of 4.27 across 22 functions. The mean complexity threshold is 4

@coderabbitai

This comment was marked as resolved.

Add `make audit` to the remaining developer-guide commit-gate
references so the required command lists match the repository-wide
quality gate set.
Merge the duplicated audit exception date failure tests into one table-driven
case while preserving the same generated Vitest case names and assertions.
codescene-delta-analysis[bot]

This comment was marked as outdated.

Add behavioural tests for audit package-data helpers, reporting re-exports,
explicit exception-date injection, and Makefile rust-audit execution. Document
new security audit modules and the cargo-audit output contract, and await the
bulk-advisory fallback path.
codescene-delta-analysis[bot]

This comment was marked as outdated.

@leynos
Copy link
Copy Markdown
Owner Author

leynos commented May 25, 2026

@coderabbitai Have the following now been resolved?

security/audit-package-data.js (1)

231-235: 🧹 Nitpick | 🔵 Trivial | 💤 Low value
Rename isPlainAdvisoryObject to isNonArrayObject or add prototype check.
The function is named "isPlainAdvisoryObject" but only delegates to isNonArrayObject, which does not verify the prototype chain. This is inconsistent with isValidTreeNode (which does check hasPlainObjectPrototype) and the name is misleading.
Either rename to reflect actual behaviour or add the prototype check for consistency:

♻️ Option 1: Rename to match behaviour
-/** Return `true` when a value is a plain (non-array, non-null) object.
+/** Return `true` when a value is a non-array, non-null object.
  * `@param` {unknown} value Value to test. `@returns` {boolean}
- * `@example` isPlainAdvisoryObject({ id: 1 }); // true
+ * `@example` isNonArrayAdvisoryObject({ id: 1 }); // true
  */
-function isPlainAdvisoryObject(value) { return isNonArrayObject(value); }
+function isNonArrayAdvisoryObject(value) { return isNonArrayObject(value); }
♻️ Option 2: Add prototype check for consistency
/** Return `true` when a value is a plain (non-array, non-null) object.
  * `@param` {unknown} value Value to test. `@returns` {boolean}
  * `@example` isPlainAdvisoryObject({ id: 1 }); // true
  */
-function isPlainAdvisoryObject(value) { return isNonArrayObject(value); }
+function isPlainAdvisoryObject(value) { return isNonArrayObject(value) && hasPlainObjectPrototype(value); }

Triage: [type:docstyle]

🤖 Detailed instructions

Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @security/audit-package-data.js around lines 231 - 235, The function
isPlainAdvisoryObject currently just calls isNonArrayObject but its name implies
a plain-object/prototype check; either rename isPlainAdvisoryObject to
isNonArrayObject everywhere or make it perform the plain-object prototype check
(use hasPlainObjectPrototype or similar) to match isValidTreeNode; update all
call sites referencing isPlainAdvisoryObject to the new name if you choose
rename, or replace its body with a combined check like isNonArrayObject(value)
&& hasPlainObjectPrototype(value) if you choose to enforce prototype validation,
and make sure to update any tests/comments that reference isPlainAdvisoryObject.

♻️ Duplicate comments (1)

scripts/security-audit-reporting.test.mjs (1)

127-156: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win
Consolidate duplicated failure-path tests to unblock the quality gate.
Line [127] and Line [143] duplicate the same test harness shape, matching the current

CodeScene duplication failure. Replace both blocks with one parameterised test.

♻️ Proposed fix
-  it('exits when an exception expires before the current date', () => {
-    const policyIo = throwingPolicyIo();
-
-    expect(() =>
-      assertNoExpired(
-        [exceptionEntry({ addedAt: '2024-01-01', expiresAt: '2024-01-31' })],
-        new Date('2024-02-01T00:00:00.000Z'),
-        policyIo,
-      ),
-    ).toThrow('exit 1');
-    expect(policyIo.error.mock.calls.map(([line]) => line)).toEqual([
-      'Audit exceptions have expired:',
-      '- exception-1 (validator) expired on 2024-01-31',
-    ]);
-  });
-
-  it('exits when an exception date range is inverted', () => {
-    const policyIo = throwingPolicyIo();
-
-    expect(() =>
-      assertNoExpired(
-        [exceptionEntry({ addedAt: '2024-02-01', expiresAt: '2024-01-31' })],
-        new Date('2024-01-15T00:00:00.000Z'),
-        policyIo,
-      ),
-    ).toThrow('exit 1');
-    expect(policyIo.error.mock.calls.map(([line]) => line)).toEqual([
-      'Audit exceptions have invalid date ranges (addedAt > expiresAt):',
-      '- exception-1 (validator) addedAt 2024-02-01 > expiresAt 2024-01-31',
-    ]);
-  });
+  it.each([
+    {
+      name: 'exits when an exception expires before the current date',
+      entry: exceptionEntry({ addedAt: '2024-01-01', expiresAt: '2024-01-31' }),
+      currentDate: new Date('2024-02-01T00:00:00.000Z'),
+      expectedLines: [
+        'Audit exceptions have expired:',
+        '- exception-1 (validator) expired on 2024-01-31',
+      ],
+    },
+    {
+      name: 'exits when an exception date range is inverted',
+      entry: exceptionEntry({ addedAt: '2024-02-01', expiresAt: '2024-01-31' }),
+      currentDate: new Date('2024-01-15T00:00:00.000Z'),
+      expectedLines: [
+        'Audit exceptions have invalid date ranges (addedAt > expiresAt):',
+        '- exception-1 (validator) addedAt 2024-02-01 > expiresAt 2024-01-31',
+      ],
+    },
+  ])('$name', ({ entry, currentDate, expectedLines }) => {
+    const policyIo = throwingPolicyIo();
+
+    expect(() => assertNoExpired([entry], currentDate, policyIo)).toThrow('exit 1');
+    expect(policyIo.error.mock.calls.map(([line]) => line)).toEqual(expectedLines);
+  });
🤖 Detailed instructions

Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @scripts/security-audit-reporting.test.mjs around lines 127 - 156, Replace
the two nearly identical it blocks that exercise assertNoExpired with a single
parameterized test that iterates the two failure scenarios; use
throwingPolicyIo(), exceptionEntry(...) and assertNoExpired(...) as the same
helpers, and for each case supply the exception params, the reference date, the
expected thrown message ('exit 1') and expected policyIo.error lines (one for
expired and one for inverted range). Ensure the new test still calls expect(()
=> assertNoExpired(...)).toThrow('exit 1') and then asserts
policyIo.error.mock.calls.map(([line]) => line) equals the appropriate array for
each scenario so behavior remains unchanged.

@leynos
Copy link
Copy Markdown
Owner Author

leynos commented May 25, 2026

@coderabbitai Have the following failed checks now been resolved?

If further work is required, please provide an AI agent prompt for the remaining work to be done to address these failures.

Do not treat warnings as optional or aspirational. Where a change is out of scope for this PR, propose a GitHub issue unless one exists already.

❌ Failed checks (1 error, 6 warnings)

Check name Status Explanation Resolution
Module-Level Documentation ❌ Error security/audit-exception-policy.js carries only a one-line module docstring that does not explain the module's purpose, utility, function, or relationship to other components. Expand security/audit-exception-policy.js's module docstring to document its purpose (exception expiry validation), exported function (assertNoExpired), and role in the audit validation pipeline with validate-audit.js.
Developer Documentation ⚠️ Warning New modules and auditIo adapter documented in developers-guide, but Makefile audit-node and rust-audit sub-targets lack explicit documentation. Document the audit-node and rust-audit Makefile targets in developers-guide, explaining their individual purposes and when each is used.
Testing (Unit And Behavioural) ⚠️ Warning Tests cover unit helper functions and Makefile recipe syntax only; lack end-to-end tests exercising actual audit workflow (cargo audit, validate-audit.js integration, exception loading). Add end-to-end tests for: (1) actual 'make audit' execution; (2) cargo audit with RUSTSEC-2023-0071 ignore verification; (3) validate-audit.js integration with security/audit-exceptions.json.
Domain Architecture ⚠️ Warning reportUnexpectedAdvisories() hardcodes console.error() instead of injecting adapter like assertNoExpired() does, leaking transport into domain. Inject reportingIo adapter parameter to reportUnexpectedAdvisories() matching audit-exception-policy.js pattern, isolating stderr output from domain logic.
Observability ⚠️ Warning Audit infrastructure lacks metrics, tracing, and structured logging across pnpm/Rust boundaries. No timing instrumentation, operation correlation IDs, or cargo-audit JSON output. Add timing metrics per phase; invoke cargo audit --json; emit advisory count metrics; implement structured JSON logs with operation/phase/status; add correlation IDs; log exception application.
Concurrency And State ⚠️ Warning The fetchBulkAdvisories timeout/abort logic lacks test coverage for when HTTP requests hang and the 30s timeout fires. Add a test to scripts/security-audit-helpers.test.mjs mocking a hung fetch, verify AbortError is caught, clearTimeout is called, and timeout message is thrown.
Architectural Complexity And Maintainability ⚠️ Warning auditIo adapter adds 838 lines without simplifying code at point of use. No production code uses it; frontend uses module mocking instead. High-CC complexity issues remain unresolved. Replace auditIo with vi.mock (frontend pattern). Merge audit-package-data.js into audit-utils.js. Use ensure_tool in Makefile's rust-audit.

@coderabbitai

This comment was marked as resolved.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 25, 2026

Tip

For best results, initiate chat on the files or code changes.

Here are the results from shell you requested:

Shell block treated as final reply content because the matching action tag was not present.

Extract the plain-object prototype predicate and use it for both dependency
tree nodes and advisory payload objects. This keeps class instances out of both
audit parsing paths while preserving null-prototype object support.
codescene-delta-analysis[bot]

This comment was marked as outdated.

@leynos
Copy link
Copy Markdown
Owner Author

leynos commented May 25, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 25, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@leynos
Copy link
Copy Markdown
Owner Author

leynos commented May 25, 2026

@coderabbitai Have the following failed checks now been resolved?

If further work is required, please provide an AI agent prompt for the remaining work to be done to address these failures.

Do not treat warnings as optional or aspirational. Where a change is out of scope for this PR, propose a GitHub issue unless one exists already.

❌ Failed checks (3 errors, 6 warnings)

Check name Status Explanation Resolution
Module-Level Documentation ❌ Error security/audit-exception-policy.js has only a single-line JSDoc, insufficient to explain module purpose, utility, function, or relationships to other components. Expand module-level JSDoc in security/audit-exception-policy.js to explain purpose (exception ledger validation), utility (assertNoExpired export), and relationships (used by validate-audit.js and test suites).
Unit Architecture ❌ Error reportUnexpectedAdvisories performs side-effects (console.error) with hard-coded dependencies, inconsistent with assertNoExpired's policyIo pattern and violating injectable dependency requirements. Make reportUnexpectedAdvisories accept optional IO adapter parameter for console.error, mirroring the policyIo pattern already applied to assertNoExpired for consistency.
Rust Compiler Lint Integrity ❌ Error UserRow.updated_at, WalkSessionRow fields, RouteRow, and ExampleDataRunRow are genuinely unused but suppressed with #[expect(dead_code)], hiding real unused code from compiler detection. Delete unused items or narrow suppression to verified-in-use code only (e.g. UserRow.created_at IS used; others are not).
Testing (Unit And Behavioural) ⚠️ Warning Critical test gaps exist: fetchBulkAdvisories timeout/abort handling is not tested, validate-audit.js has no end-to-end tests, and the full audit workflow lacks integration testing. Add tests for fetchBulkAdvisories timeout scenarios using simulated AbortError; add e2e tests for validate-audit.js exception validation; add full audit workflow integration tests covering both Node and Rust phases.
Testing (Property / Proof) ⚠️ Warning fetchBulkAdvisories introduces a timeout/abort invariant lacking property-based tests. PR comments explicitly identify missing "concurrency/timeout test coverage for fetchBulkAdvisories". Add property-based tests for the fetchBulkAdvisories timeout invariant using fast-check to verify AbortController timeout handling, request abortion, and cleanup logic across various timing scenarios.
Domain Architecture ⚠️ Warning reportUnexpectedAdvisories hardcodes console.error, violating domain/adapter separation; should use dependency injection like assertNoExpired's policyIo pattern. Add reportingIo parameter to reportUnexpectedAdvisories with defaultReportingIo adapter wrapping console.error, matching assertNoExpired's injection pattern for consistent boundaries.
Observability ⚠️ Warning Rust audit and bulk advisory fallback lack production observability: no context-aware logging at decision/failure points, no metrics, minimal error context in thrown messages. Add logging at failure boundaries (bulk advisory fetch, timeouts, spawn failures) with operation names and timing; include error categories; document key log points for maintainers to monitor.
Concurrency And State ⚠️ Warning fetchBulkAdvisories async timeout/abort handling lacks test coverage for race conditions during response.text() awaiting and AbortError propagation—risky interleaving not exercised. Add test for fetchBulkAdvisories timeout firing during fetch or response.text(); assert AbortError is caught and converted to timeout message correctly.
Architectural Complexity And Maintainability ⚠️ Warning audit-package-data publicly exported (306 LOC) but only imported by audit-utils with no reuse strategy. High-CC functions unrefactored despite PR objectives. Make audit-package-data private to audit-utils; refactor high-CC functions per PR notes; replace rust-audit guard with ensure_tool.

@coderabbitai

This comment was marked as resolved.

@leynos
Copy link
Copy Markdown
Owner Author

leynos commented May 25, 2026

@coderabbitai Have the following failed checks now been resolved?

If further work is required, please provide an AI agent prompt for the remaining work to be done to address these failures.

Do not treat warnings as optional or aspirational. Where a change is out of scope for this PR, propose a GitHub issue unless one exists already.

❌ Failed checks (3 errors, 6 warnings)

Check name Status Explanation Resolution
Module-Level Documentation ❌ Error security/audit-exception-policy.js has only a single-line JSDoc, insufficient to explain module purpose, utility, function, or relationships to other components. Expand module-level JSDoc in security/audit-exception-policy.js to explain purpose (exception ledger validation), utility (assertNoExpired export), and relationships (used by validate-audit.js and test suites).
Unit Architecture ❌ Error reportUnexpectedAdvisories performs side-effects (console.error) with hard-coded dependencies, inconsistent with assertNoExpired's policyIo pattern and violating injectable dependency requirements. Make reportUnexpectedAdvisories accept optional IO adapter parameter for console.error, mirroring the policyIo pattern already applied to assertNoExpired for consistency.
Rust Compiler Lint Integrity ❌ Error UserRow.updated_at, WalkSessionRow fields, RouteRow, and ExampleDataRunRow are genuinely unused but suppressed with #[expect(dead_code)], hiding real unused code from compiler detection. Delete unused items or narrow suppression to verified-in-use code only (e.g. UserRow.created_at IS used; others are not).
Testing (Unit And Behavioural) ⚠️ Warning Critical test gaps exist: fetchBulkAdvisories timeout/abort handling is not tested, validate-audit.js has no end-to-end tests, and the full audit workflow lacks integration testing. Add tests for fetchBulkAdvisories timeout scenarios using simulated AbortError; add e2e tests for validate-audit.js exception validation; add full audit workflow integration tests covering both Node and Rust phases.
Testing (Property / Proof) ⚠️ Warning fetchBulkAdvisories introduces a timeout/abort invariant lacking property-based tests. PR comments explicitly identify missing "concurrency/timeout test coverage for fetchBulkAdvisories". Add property-based tests for the fetchBulkAdvisories timeout invariant using fast-check to verify AbortController timeout handling, request abortion, and cleanup logic across various timing scenarios.
Domain Architecture ⚠️ Warning reportUnexpectedAdvisories hardcodes console.error, violating domain/adapter separation; should use dependency injection like assertNoExpired's policyIo pattern. Add reportingIo parameter to reportUnexpectedAdvisories with defaultReportingIo adapter wrapping console.error, matching assertNoExpired's injection pattern for consistent boundaries.
Observability ⚠️ Warning Rust audit and bulk advisory fallback lack production observability: no context-aware logging at decision/failure points, no metrics, minimal error context in thrown messages. Add logging at failure boundaries (bulk advisory fetch, timeouts, spawn failures) with operation names and timing; include error categories; document key log points for maintainers to monitor.
Concurrency And State ⚠️ Warning fetchBulkAdvisories async timeout/abort handling lacks test coverage for race conditions during response.text() awaiting and AbortError propagation—risky interleaving not exercised. Add test for fetchBulkAdvisories timeout firing during fetch or response.text(); assert AbortError is caught and converted to timeout message correctly.
Architectural Complexity And Maintainability ⚠️ Warning audit-package-data publicly exported (306 LOC) but only imported by audit-utils with no reuse strategy. High-CC functions unrefactored despite PR objectives. Make audit-package-data private to audit-utils; refactor high-CC functions per PR notes; replace rust-audit guard with ensure_tool.

@coderabbitai

This comment was marked as resolved.

@leynos
Copy link
Copy Markdown
Owner Author

leynos commented May 25, 2026

@coderabbitai please create a new GitHub issue for the following:

Issue 1 — Rust Compiler Lint Integrity

Track removal of #[expect(dead_code)] suppressions on RouteRow, ExampleDataRunRow, UserRow.updated_at, and WalkSessionRow.created_at/updated_at when the corresponding docs/backend-roadmap.md 3.5.1 items are implemented. The suppressions are correctly using #[expect] (which auto-fails when no longer needed), so no action is required now — this issue simply ensures they are removed when the roadmap items land.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 25, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
gh issue create \
  --repo leynos/wildside \
  --title "Remove #[expect(dead_code)] suppressions once roadmap item 3.5.1 lands" \
  --assignee leynos \
  --body "## Summary

Track and remove \`#[expect(dead_code)]\` suppressions on the following items once the corresponding \`docs/backend-roadmap.md 3.5.1\` work is implemented:

- \`RouteRow\`
- \`ExampleDataRunRow\`
- \`UserRow.updated_at\`
- \`WalkSessionRow.created_at\` and \`WalkSessionRow.updated_at\`

## Context

The suppressions are correctly using \`#[expect(dead_code)]\` (which will cause a compile error automatically once the suppressed item is used), so no action is required before the roadmap items land. This issue exists solely to ensure clean-up is not forgotten when roadmap item 3.5.1 is implemented.

Raised during review of PR `#354` by `@leynos`."

Length of output: 924



@leynos — issue created: #360

#360 — Remove #[expect(dead_code)] suppressions once roadmap item 3.5.1 lands has been opened and assigned to you. No action is required now — the #[expect] attributes will automatically trigger a compile error once the suppressed items are put to use, at which point this issue serves as the clean-up prompt.

Document audit exception policy responsibilities, route advisory reporting
through an injected IO adapter, cover bulk-advisory abort cleanup, and reuse
the shared Makefile tool guard for cargo-audit.
codescene-delta-analysis[bot]

This comment was marked as outdated.

@leynos
Copy link
Copy Markdown
Owner Author

leynos commented May 26, 2026

@coderabbitai please provide an AI agent prompt for addition of a reference to #360 to relevant expectation reasons in the code

@coderabbitai

This comment was marked as resolved.

Add the tracked GitHub issue to the roadmap 3.5.1 dead-code
expectations for generated schema and persistence timestamp fields.
Keep the cleanup obligation traceable from the source without changing
runtime behaviour.
codescene-delta-analysis[bot]

This comment was marked as outdated.

@leynos
Copy link
Copy Markdown
Owner Author

leynos commented May 26, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 26, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@scripts/security-audit-helpers.test.mjs`:
- Around line 451-455: The test is swallowing all exceptions from runAuditJson
which hides regressions; update the property test to assert behavior based on
shouldAbort: when shouldAbort is true, assert runAuditJson(auditIo) rejects with
a timeout error matching /Bulk advisory audit timed out after \d+ms at/; when
shouldAbort is false, assert runAuditJson(auditIo) resolves and returns an
object with status: 0 (use expect(...).rejects.toThrow(...) and
expect(...).resolves.toMatchObject(...) respectively) instead of the empty
try/catch around runAuditJson.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 9e1933f1-d453-403f-8915-2634b4bc790a

📥 Commits

Reviewing files that changed from the base of the PR and between ef395da and af495b3.

📒 Files selected for processing (8)
  • Makefile
  • backend/src/inbound/http/schemas.rs
  • backend/src/outbound/persistence/models.rs
  • scripts/makefile-audit.test.mjs
  • scripts/security-audit-helpers.test.mjs
  • scripts/security-audit-reporting.test.mjs
  • security/audit-exception-policy.js
  • security/audit-reporting.js

Comment thread scripts/security-audit-helpers.test.mjs Outdated
Replace the catch-all in the bulk advisory timeout property test with
explicit expectations for each generated case. The aborting path must reject
with the timeout message, while the successful path must resolve with a clean
audit status.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant