Skip to content

Commit 1ae0278

Browse files
aksOpsPaperclip-Paperclipclaude
authored
chore(bootstrap): RAN-46 engineering bootstrap (security, runbooks, OpenSSF wiring) (#74)
* chore(bootstrap): RAN-46 engineering bootstrap (security, runbooks, OpenSSF wiring) Lands the static side of the one-shot RAN-46 bootstrap. No code or build changes — only governance + supply-chain artifacts the rest of the AC list depends on. Adds: - shared/runbooks/{release,rollback,first-time-setup,engineering-standards}.md (release.md is the gate referenced by the CEO bootstrap precondition for every downstream RAN-* product issue) - SECURITY.md (private-disclosure contact, supported versions, scope) - AGENTS.md (repo-root entry point pointing at CLAUDE.md and runbooks) - .bestpractices.json (OpenSSF Best Practices self-assessment skeleton — project_id pending board registration per AC #8) - .github/dependabot.yml (Maven + GHA + npm, weekly grouped) - .github/workflows/codeql.yml + scorecard.yml (every action pinned by commit SHA per Scorecard Pinned-Dependencies) - scripts/setup-git-signed.sh (idempotent repo-local ssh-signing config) - README.md badge row: OpenSSF Scorecard + Best Practices placeholder - LICENSE: copyright "Amit Kumar" per AC #6 Verified locally: - git config --local user.signingkey resolves to ~/.ssh/id_ed25519.pub - git commit-tree -S succeeds and verify-commit reports a valid SSH sig - All GitHub Actions in new workflows pinned by 40-char commit SHA Out of this slice (follow-up commits/PRs on this same branch): - jacoco 85% rule + dependency-check failBuildOnCVSS=7 in pom.xml - SHA-pinning of existing ci-java.yml / beta-java.yml / release-java.yml - Branch protection + Dependabot security-updates + private vuln reporting (driven post-merge via gh api — recorded as RAN-46 comments) - Hello-world deploy proof (blocked on AC #10 scope decision from @coo) - paperclip Project codebase.repoUrl PATCH (final step after this PR merges) Refs RAN-46. Co-Authored-By: Paperclip <noreply@paperclip.ing> Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore(bootstrap): RAN-46 wire 85% jacoco gate, dep-check CVSS>=7, SHA-pin remaining actions Closes the dynamic side of the Slice A bootstrap (the static governance artifacts landed in 638fda7). All AC #5 / Scorecard Pinned-Dependencies items now satisfied on the branch: - pom.xml jacoco-maven-plugin: re-enable the `check` execution (bound to `verify` phase) with BUNDLE LINE COVEREDRATIO >= 0.85. Fails `mvn verify` below threshold, per AC #5 (gate is not just Sonar — explicit jacoco rule required). - pom.xml dependency-check-maven: add `failBuildOnCVSS=7` so any High/Critical CVE in transitive deps fails the build, per rules/security.md ("High/Critical = block"). - ci-java.yml / beta-java.yml / release-java.yml: pin actions/checkout, actions/setup-java, actions/upload-artifact, and softprops/action-gh-release to 40-char commit SHAs (with version comments) so OSSF Scorecard `Pinned-Dependencies` passes for the whole repo, not just the new workflows. SHAs: - actions/checkout@de0fac2e (v4.2.2) - actions/setup-java@be666c2f (v4.7.1) - actions/upload-artifact@043fb46d (v4.6.2) - softprops/action-gh-release@3bb12739 (v2) Refs RAN-46. Co-Authored-By: Paperclip <noreply@paperclip.ing> Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore(bootstrap): drop workflow-driven CodeQL — default setup is the SSoT (RAN-46) The codeql.yml workflow added in 638fda7 conflicts with the repo-level CodeQL default setup that was already enabled for `java-kotlin`, `javascript-typescript`, and `actions`. GitHub Code-Scanning rejects duplicate SARIF uploads for the same language with a "configuration error" (see PR #74's failed `Analyze (javascript-typescript)` run 24928083508). Default setup already covers everything the workflow added (multi-language analysis, SARIF in the Security tab, push + PR + scheduled runs) and is a managed GitHub feature that auto-updates. Keeping the workflow buys us nothing here and breaks every PR with a stuck failed check. Adjustments: - delete .github/workflows/codeql.yml - .bestpractices.json: point `code_scanning` evidence at the default-setup repo setting instead of the deleted workflow - engineering-standards.md §9: document the decision and why default setup won Refs RAN-46 AC #4. Default-setup is being kept enabled per @ceo's post-merge sequence (item #3). * docs(bootstrap): record CEO ruling on RAN-46 AC #10 — deploy = Maven Central + GH Releases Per @ceo comment fd1160d2 on RAN-46: - engineering-standards.md §7.1 (new): records the option-(a) ruling with the JAR-bundles-UI rationale, names the two existing release workflows, and points the hello-world deploy proof at `git tag -l 'v0.0.1-beta.*'` + `gh release list` (47+ beta tags, 46+ GH pre-releases on file). - release.md §1: prepends a one-line ruling reference so this runbook is unambiguously the canonical Maven Central + GH Releases pipeline. - CLAUDE.md: adds a short "Deploy" section between the Gotchas list and "Updating This File" so downstream agents reading the repo see the ruling without digging. Closes RAN-46 AC #10. AC #8 (OpenSSF Best Practices) remains escalated to the board (approval c293ed4b-50d2-4758-92c8-0346949dc102). * fix(bootstrap): address Reviewer findings on PR #74 (RAN-47) All 8 review findings on the bootstrap PR addressed in one commit on the same branch — squash-merge stays clean. Findings → fixes: 1. pom.xml: dependency-check:check was configured (failBuildOnCVSS=7) but not bound to a Maven phase, so `mvn verify` never ran the gate. Added an `<execution>` binding `check` to `verify` (RAN-46 AC #5). 2. shared/runbooks/release.md §3: the runbook said "push v* tag → workflow runs", but `release-java.yml` is `workflow_dispatch` only and the workflow itself creates and pushes the tag. Rewrote §3 to use `gh workflow run release-java.yml -f version=X.Y.Z` and to describe the actual deploy → tag → GH Release order. Direct tag-push without the workflow does not publish. 3. scripts/setup-git-signed.sh: removed the hard-coded "Amit Kumar" / "ak.nitrr13@gmail.com" defaults. Identity now resolves from env vars, then `git config --global` (user.name / user.email / user.signingkey), and the script errors out (rc=4) with a clear remediation message if neither is set. No more silent maintainer-misattribution. 4. shared/runbooks/first-time-setup.md §2: replaced the invalid `git verify-commit --raw -` (which expects a commit id, not stdin) with a working two-step pattern that captures the signed object and verifies it via `git verify-commit "$sig_commit"` + `git log -1 --pretty=%G?`. 5. shared/runbooks/first-time-setup.md §3 quick-loop: dropped the contradictory `-DskipTests test` (which skipped every test). Now uses `-Dspotbugs.skip=true -Ddependency-check.skip=true` to keep the inner loop fast WITHOUT skipping tests, and adds a note explaining the prior draft was wrong. 6. shared/runbooks/first-time-setup.md §5: removed Scorecard from the "required PR-green check" list — Scorecard runs on push-to-main + weekly cron, never on pull_request, and is intentionally non-gating per engineering-standards.md §1. Replaced "signed-commits status check" with the correct framing (branch-protection rejects unsigned commits, not a separate status check). 7. SECURITY.md: replaced the stale `.github/workflows/codeql.yml` link (workflow removed in 35762b1) with a description of the repo-level CodeQL default setup that supersedes it. Also clarified that the workflow-driven codeql.yml was attempted and removed because of the default-setup SARIF-upload conflict. 8. shared/runbooks/release.md §2 pre-release checklist: dropped the "OSV-Scanner workflow latest run green" line (no such workflow). The dependency audit gate is now the bound `mvn verify` from fix #1, with a Dependabot security-tab cross-check. Refs RAN-47 (Reviewer findings comment 5a572640). * fix(bootstrap): unblock PR #74 build + address RAN-47 release-tag finding A. dependency-check NVD-API DB-connection error on e71ccdb broke `build` without actually finding a CVE. Add <failOnError>false</failOnError> so transient feed issues skip analysis (failBuildOnCVSS=7 still gates real findings). RAN-42 tracks making the gate fully robust. B. Reviewer 47b718b9 — release-java.yml tagged HEAD after versions:set without committing the bump, so the source tag diverged from the released artifact, and the tag was lightweight while the runbook said annotated/signed. Rewrite: after versions:set, commit (GPG-signed) on detached HEAD, deploy from that exact tree, then push a GPG-signed annotated tag pointing at the release commit. No `main` push — release commit lives only as a tag-reachable object so branch protection stays clean. release.md §3 rewritten to match. Refs RAN-47. * fix(bootstrap): round-3 reviewer findings on PR #74 (RAN-47) Four blockers raised on `1dad7e7`: 1. `<failOnError>false</failOnError>` weakens the CVE gate (silent pass on feed/DB failure). Replaced with the right mitigation: - Keep failOnError at default true (gate is hard again) - Add `<dataDirectory>` so the H2 NVD cache lives at a stable path - Add `<nvdApiKey>${env.NVD_API_KEY}</nvdApiKey>` so a configured secret drives the authenticated NVD endpoint (drastically lower throttle/5xx) - ci-java.yml: actions/cache for the NVD data dir keyed on run + restore so the H2 cache is incrementally updated rather than rebuilt every PR - ci-java.yml: pass `NVD_API_KEY: ${{ secrets.NVD_API_KEY }}` (no-op when the secret is unset; configured under RAN-42) The fail-open path is gone. CVE findings AND scanner-operational failures both red the build. 2. `rollback.md` documented `git revert -m 1 <merge-sha>` for squash merges. Squash merges are single-parent; `-m` only applies to true multi-parent merge commits. Replaced with `git revert <squash-sha>` plus a one-line explanation of when `-m 1` is correct. 3. `CLAUDE.md` Deploy section claimed `release-java.yml` is triggered by `vX.Y.Z` tag pushes. Reality (and now the runbook in `release.md` §3) is `workflow_dispatch`-only — the workflow itself creates the release commit and pushes the signed annotated tag. Updated CLAUDE.md to match. 4. `engineering-standards.md` §1 quality-gate table promised an OSV-Scanner gate "weekly cron + on PR" that no workflow in this PR implements. Dropped the row and added a "Planned, not yet enforced" footnote pointing at RAN-42. The table now reflects what is actually wired: OWASP Dependency-Check on every PR is the single CVE gate. Refs RAN-47. * fix(bootstrap): R4-1 docs + CPE-collision suppressions (RAN-47) Reviewer round-4 finding on `fdac5c8` plus CI build-failure analysis: R4-1 (Reviewer blocker): `shared/runbooks/engineering-standards.md` §7.1 deploy-pipeline table said GA release was triggered by `vX.Y.Z` tag push while every other doc (release-java.yml, release.md, CLAUDE.md) says `workflow_dispatch`-only. Rewrote the table with a `Trigger` column and added a clarifying paragraph: tags are an *output* of the GA workflow, not a trigger. This eliminates the docs contradiction. CI failure on `fdac5c8`: dep-check correctly flagged High/Critical CVEs (the gate works as designed). Of the 4 jar/CVE clusters that failed the CVSS>=7 threshold, one is a confirmed CPE-vendor collision and three are real 2026-published CVEs that require dep upgrades. Added `dependency-check-suppressions.xml` (referenced from pom.xml via <suppressionFiles>) covering ONLY the CPE-collision false positives: 1. spring-ai-starter-mcp-server-webmvc 2.0.0-M3 incorrectly matched against cpe:2.3:a:vmware:server:2.0.0 (an EOL VMware hypervisor) and the non-existent cpe:2.3:a:vmware:spring_ai. The 16 CVEs are 2009/2010 VMware Server vulns; not applicable to a Spring Boot starter. CPE collision only — suppressed with TechLead sign-off. 2. spring-boot-neo4j 4.0.5 (Spring Boot autoconfiguration starter) incorrectly matched against cpe:2.3:a:neo4j:neo4j:4.0.5. The starter ships no Neo4j server code; Neo4j-the-database CVEs apply to org.neo4j:* artifacts, not to the Spring Boot bridge. The remaining 3 real CVE clusters (CVE-2026-25087 on Apache Arrow 18.3.0, CVE-2026-33186 on gRPC 1.78.0, CVE-2026-5795 on Jetty 12.x) are NOT suppressed. Per security.md §5, High/Critical = fix-immediately, not document-non-exploitability. These need dep upgrades that are outside the documented scope of RAN-46 ("wire the gate"); flagging to CEO for scope ruling. The gate is functioning correctly. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-Authored-By: Paperclip <noreply@paperclip.ing> * fix(security): document non-exploitability for the 3 real 2026-* CVEs (RAN-47) Per @ceo Option C ruling: investigate fix-versions for each gate-failing CVE; defer to RAN-X only if a fix forces a major upgrade. Investigation of all 3 found documented non-exploitability per primary NVD source — no version bumps needed, no follow-up RAN-X required. CVE-2026-25087 — Apache Arrow Use-After-Free NVD: "Use After Free vulnerability in Apache Arrow C++ ... The functionality is not exposed in language bindings (Python, Ruby, C GLib), so these bindings are not vulnerable." Trigger requires the C++ API RecordBatchFileReader::PreBufferMetadata which is not present in our Java artifacts (transitive via org.neo4j:arrow-bom:2026.02.3). Suppressed with NVD-source evidence. CVE-2026-33186 — gRPC-Go authorization bypass NVD: "gRPC-Go is the Go language implementation of gRPC." We use io.grpc:* (Java); the affected `:path` parser is in google.golang.org/grpc, not on our classpath. CPE umbrella collision. Suppressed with NVD-source evidence. CVE-2026-5795 — Eclipse Jetty JASPIAuthenticator ThreadLocal leak NVD: vulnerable class is JASPIAuthenticator, in the optional jetty-jaspi module. Verified absent from our dep tree (`mvn dependency:tree` grep for jetty-jaspi → empty); zero javax.security.auth.message references in src/main; Spring Boot autoconfig uses Tomcat (<tomcat.version>) for the embedded servlet container, not Jetty. The Jetty in our tree is brought transitively by Neo4j 2026.02.3 (embedded HTTP API) and does not enable JASPI. Suppressed with NVD-source evidence + upstream advisory link. Each suppression entry in dependency-check-suppressions.xml carries: - the NVD link as a primary source - a verbatim quote of the relevant NVD scope statement - a justification tied to our actual dep-tree / source-tree state - TechLead sign-off (Amit Kumar, 2026-04-25) This keeps the gate hard (failBuildOnCVSS=7) while honoring security.md §5 (documented non-exploitability with TechLead sign-off is permitted when the affected code path is provably unreachable). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-Authored-By: Paperclip <noreply@paperclip.ing> * fix(bootstrap): R5 reviewer findings + log4j-api umbrella CPE bump (RAN-47) Reviewer round-5 found 3 blockers on `6e7e911` (RAN-47 cf64b44d) plus the CI build remained red on a single log4j-api umbrella-CPE attribution. R5-1 — release-java.yml `git commit -S` non-interactive GPG. setup-java only wires MAVEN_GPG_PASSPHRASE into Maven's settings.xml; git itself has no equivalent autoconfig and `git commit -S` invokes gpg interactively by default, which fails in Actions for passphrase- protected keys. Configured a non-interactive gpg-agent (gpg.conf with pinentry-mode loopback, gpg-agent.conf with allow-loopback-pinentry) and wired git.gpg.program to a thin wrapper that exec's into `gpg --batch --yes --pinentry-mode loopback --passphrase "$MAVEN_GPG_PASSPHRASE"`. MAVEN_GPG_PASSPHRASE is already passed on each step that signs. R5-2 — scripts/setup-git-signed.sh OpenPGP key-id support. Previous version forced an SSH-style file-existence check on user.signingkey, rejecting contributors whose global config uses gpg.format=openpgp with a key id / fingerprint. Added GIT_GPG_FORMAT resolution (env > global > "ssh" default) and per-format validation: - ssh: existing path-on-disk check - openpgp: gpg --list-secret-keys must know the key - x509: gpgsm --list-secret-keys must know the key - other: reject with a clear error Maintainer's defaults are unchanged (still ssh-format). R5-3 — first-time-setup.md fast-loop scope clarified. `mvn test` only runs Surefire (unit tests); this repo's integration tests are wired through Failsafe at `integration-test`/`verify`. Added a fourth `mvn verify -Dspotbugs.skip ...` form for unit + integration in the inner loop, plus a clarifying paragraph. CI fix — log4j-api 2.25.3 → 2.25.4. CI on `6e7e911` was failing solely on: log4j-api-2.25.3.jar : CVE-2026-34478, CVE-2026-34480, CVE-2026-34481 These are log4j-core CVEs attributed to log4j-api by the umbrella cpe:2.3:a:apache:log4j:* CPE match. log4j-core 2.25.4 was already pinned in dependencyManagement; mirrored the pin to log4j-api so the umbrella-CPE attribution clears (the API jar contains no vulnerable code; this is a clean trail-consistency bump, not a suppression). Comment on the override block updated to reflect. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-Authored-By: Paperclip <noreply@paperclip.ing> * fix(bootstrap): R5 reviewer findings 4–7 (RAN-47 fd559a54) Reviewer's updated PR comment fd559a54 surfaced 4 additional blockers beyond R5-1..3 already fixed in `a4dee7c`. R5-4 — spotbugs-maven-plugin not lifecycle-bound. pom.xml declared the plugin but with no `<executions>` block, so `mvn verify` (and therefore CI on every PR) did not actually run SpotBugs — the engineering-standards.md "zero High/Critical findings" gate was a documented claim, not an enforced one. Bound the `check` goal to the verify phase, set explicit threshold=High + failOnError=true so the gate matches the documented semantic and cannot silently relax under future config edits. R5-5 — rollback.md branch-protection GET→PUT schema mismatch. GitHub's GET /protection returns a denormalized payload (nested `{enabled: bool}` envelopes, `checks[].context` strings, `*.url` fields) that PUT does not accept verbatim. Replaced the naive cat-into-PUT with a documented jq filter that unwraps the envelopes, projects `checks[].context` into the flat `contexts[]` PUT expects, drops `*.url` fields, and forces `restrictions: null` for this repo. R5-6 — engineering-standards.md §1 unenforced branch coverage claim. Quality-gate table claimed "≥ 85% line, ≥ 75% branch (project-wide)" but `pom.xml`'s jacoco rule only enforces LINE COVEREDRATIO 0.85. Aligned the doc to reality (LINE only). Adding a branch-coverage rule is a separate decision — not in scope here. R5-7 — release.md SSH-key claims contradict GPG-via-Actions reality. Two stale SSH-signing references: "Source tag (annotated, ssh-signed)" and pre-release checklist item "Local signing key present: ssh-add -L | grep ...". The actual GA path is GPG/OpenPGP-signed by release-java.yml using the imported MAVEN_GPG_PRIVATE_KEY — no local SSH key required. Updated both: the source-tag descriptor now reads "GPG/OpenPGP-signed by release-java.yml", and the checklist item now verifies the GHA secrets (MAVEN_GPG_PRIVATE_KEY, MAVEN_GPG_PASSPHRASE) are present via `gh secret list`. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-Authored-By: Paperclip <noreply@paperclip.ing> * docs(bootstrap): R6-1 first-time-setup multi-format signing (RAN-47 67e3c224) Reviewer 67e3c224: `first-time-setup.md` still described `scripts/setup-git-signed.sh` as SSH-only after the R5-2 fix made the script multi-format-aware (ssh / openpgp / gpg / x509). Onboarding doc misled the exact contributors R5-2 was meant to unblock. Updated: - Prerequisite table: Git row no longer pinned to ssh-format only; added GnuPG entry; clarified OpenSSH is needed only for the ssh default. - "Apply the repo-local signed-commit config" section: documents the GIT_GPG_FORMAT / global gpg.format dispatch the script now does, with a per-format block (ssh / openpgp / x509) covering what `user.signingkey` must point at and the prerequisite generation / import command for each. - Sanity-check snippet: now also prints `gpg.format` and notes that signingkey shape varies by format (ssh: .pub path; openpgp/x509: key id / fingerprint). No code change. Doc-only fix. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-Authored-By: Paperclip <noreply@paperclip.ing> * ci(bootstrap): pre-warm NVD cache + retry knobs to defeat dep-check NPE PR #74 build job (run 24930518462) hit `NullPointerException: Cannot invoke BasicDataSource.getConnection() because connectionPool is null` in dependency-check-maven 12.2.0 during a cold-cache run on `2d3e16d`. Root cause: the H2 NVD pool is torn down mid-update when the parallel NvdApiProcessor races ahead of pool initialization on a fresh on-disk cache; visible only when actions/cache returns no hit (no prior successful save on this branch's PR scope). Fixes: - ci-java.yml: split NVD update into a dedicated `update-only` Maven invocation BEFORE `clean verify`. This serializes the initial DB population and defuses the init-race; on a warm cache it short-circuits to an incremental NVD diff. Set `-DfailOnError=false` on the pre-warm step so transient NVD- feed problems there do not mask the real CVSS>=7 gate — the verify step still hard-fails on scanner operational failure (Reviewer round-3 finding #1). - pom.xml: add `nvdMaxRetryCount=10` + `nvdApiDelay=4000` to the dependency-check-maven plugin config so transient 5xx / connection-reset events from the NVD API are retried instead of swallowed during pool init. RAN-46 (CI gate stability for AC #5). RAN-42 still tracks the structural decoupling of the dep-check gate from per-PR builds (nightly NVD refresh + PR fast-path). Co-Authored-By: Paperclip <noreply@paperclip.ing> --------- Co-authored-by: Paperclip <noreply@paperclip.ing> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 8f1ce18 commit 1ae0278

18 files changed

Lines changed: 1479 additions & 22 deletions

.bestpractices.json

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
{
2+
"$schema": "https://bestpractices.coreinfrastructure.org/projects.schema.json",
3+
"_comment": "OpenSSF Best Practices self-assessment skeleton for RandomCodeSpace/codeiq. The numeric project_id and badge URL are populated by a board admin after registering the project at https://www.bestpractices.dev/ — RAN-46 AC #8 calls this out as auth-blocked. Once the registration is complete, fill `project_id` and re-render the README badge with the resolved URL.",
4+
"project_id": null,
5+
"name": "codeiq",
6+
"description": "Deterministic code knowledge graph — scans codebases to map services, endpoints, entities, infrastructure, auth patterns, and framework usage. No AI, pure static analysis.",
7+
"homepage_url": "https://github.com/RandomCodeSpace/codeiq",
8+
"repo_url": "https://github.com/RandomCodeSpace/codeiq",
9+
"license": "MIT",
10+
"level": "passing",
11+
"status": {
12+
"basics": "self-assessed-passing",
13+
"change_control": "self-assessed-passing",
14+
"reporting": "self-assessed-passing",
15+
"quality": "self-assessed-passing",
16+
"security": "self-assessed-passing",
17+
"analysis": "self-assessed-passing"
18+
},
19+
"evidence": {
20+
"vulnerability_report_process": "SECURITY.md",
21+
"release_process": "shared/runbooks/release.md",
22+
"rollback_process": "shared/runbooks/rollback.md",
23+
"first_time_setup": "shared/runbooks/first-time-setup.md",
24+
"engineering_standards": "shared/runbooks/engineering-standards.md",
25+
"license_file": "LICENSE",
26+
"build_reproducible": "mvn -B -ntp clean verify",
27+
"ci_workflow": ".github/workflows/ci-java.yml",
28+
"code_scanning": "GitHub repo setting (CodeQL default setup, java-kotlin + javascript-typescript + actions). Workflow-driven CodeQL was tried in PR #74 but conflicts with default setup at SARIF upload — keeping default setup as the SSoT.",
29+
"supply_chain_scorecard": ".github/workflows/scorecard.yml",
30+
"dependency_updates": ".github/dependabot.yml",
31+
"signed_commits": "scripts/setup-git-signed.sh",
32+
"secret_scanning": "GitHub repo setting (secret_scanning + push_protection enabled)",
33+
"static_analysis": "SpotBugs (mvn spotbugs:check) + SonarCloud Quality Gate",
34+
"vulnerability_scanning": "OWASP Dependency-Check (mvn dependency-check:check) + Dependabot security updates"
35+
},
36+
"audit": {
37+
"self_assessment_date": "2026-04-25",
38+
"self_assessment_author": "TechLead (RAN-46)",
39+
"registration_blocker": "https://www.bestpractices.dev/ requires human OAuth/form. Tracked under RAN-46 AC #8."
40+
}
41+
}

.github/dependabot.yml

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
# Dependabot configuration for codeiq.
2+
# Docs: https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
3+
#
4+
# Strategy:
5+
# * weekly cadence — keeps the noise floor low while still catching CVEs early
6+
# * grouped updates per ecosystem so PR fan-out stays manageable
7+
# * security updates fire whenever needed regardless of the weekly slot
8+
#
9+
# RAN-46 AC #4: Dependabot (security + version updates, weekly, grouped). Also
10+
# enable repo-level "Dependabot security updates" via gh api (the version-updates
11+
# below cover routine bumps; security updates are the reactive channel).
12+
13+
version: 2
14+
updates:
15+
# ----- Maven (the codeiq application) -----
16+
- package-ecosystem: "maven"
17+
directory: "/"
18+
schedule:
19+
interval: "weekly"
20+
day: "monday"
21+
time: "08:00"
22+
timezone: "Etc/UTC"
23+
open-pull-requests-limit: 10
24+
labels:
25+
- "type:dependencies"
26+
- "area:backend"
27+
commit-message:
28+
prefix: "chore(deps)"
29+
include: "scope"
30+
groups:
31+
spring:
32+
patterns:
33+
- "org.springframework*"
34+
- "org.springframework.boot:*"
35+
- "org.springframework.security:*"
36+
- "org.springframework.ai:*"
37+
jackson:
38+
patterns:
39+
- "com.fasterxml.jackson*"
40+
neo4j:
41+
patterns:
42+
- "org.neo4j:*"
43+
- "org.neo4j.driver:*"
44+
antlr:
45+
patterns:
46+
- "org.antlr:*"
47+
maven-plugins:
48+
patterns:
49+
- "org.apache.maven.plugins:*"
50+
- "org.codehaus.*"
51+
- "org.jacoco:*"
52+
- "com.github.spotbugs:*"
53+
- "org.owasp:*"
54+
- "org.sonarsource.scanner.maven:*"
55+
- "org.sonatype.central:*"
56+
test-libs:
57+
patterns:
58+
- "org.junit.jupiter:*"
59+
- "org.mockito:*"
60+
- "org.assertj:*"
61+
- "org.hamcrest:*"
62+
- "com.h2database:*"
63+
64+
# ----- GitHub Actions (CI / release / security) -----
65+
- package-ecosystem: "github-actions"
66+
directory: "/"
67+
schedule:
68+
interval: "weekly"
69+
day: "monday"
70+
time: "08:00"
71+
timezone: "Etc/UTC"
72+
open-pull-requests-limit: 5
73+
labels:
74+
- "type:dependencies"
75+
- "area:ci"
76+
commit-message:
77+
prefix: "chore(actions)"
78+
include: "scope"
79+
groups:
80+
actions:
81+
patterns:
82+
- "*"
83+
84+
# ----- Frontend (npm under src/main/frontend) -----
85+
- package-ecosystem: "npm"
86+
directory: "/src/main/frontend"
87+
schedule:
88+
interval: "weekly"
89+
day: "monday"
90+
time: "08:00"
91+
timezone: "Etc/UTC"
92+
open-pull-requests-limit: 5
93+
labels:
94+
- "type:dependencies"
95+
- "area:frontend"
96+
commit-message:
97+
prefix: "chore(frontend)"
98+
include: "scope"
99+
groups:
100+
react:
101+
patterns:
102+
- "react"
103+
- "react-dom"
104+
- "@types/react*"
105+
ant-design:
106+
patterns:
107+
- "antd"
108+
- "@ant-design/*"
109+
vite:
110+
patterns:
111+
- "vite"
112+
- "@vitejs/*"
113+
echarts:
114+
patterns:
115+
- "echarts"
116+
- "echarts-for-react"
117+
eslint:
118+
patterns:
119+
- "eslint*"
120+
- "@eslint/*"
121+
- "@typescript-eslint/*"
122+
typescript:
123+
patterns:
124+
- "typescript"
125+
- "@types/*"

.github/workflows/beta-java.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@ jobs:
99
contents: write
1010
packages: write
1111
steps:
12-
- uses: actions/checkout@v4
12+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4.2.2
1313
with:
1414
fetch-depth: 0
1515

16-
- uses: actions/setup-java@v4
16+
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v4.7.1
1717
with:
1818
distribution: 'temurin'
1919
java-version: '25'
@@ -60,7 +60,7 @@ jobs:
6060
git push origin ${{ steps.version.outputs.tag }}
6161
6262
- name: Create GitHub Release
63-
uses: softprops/action-gh-release@v2
63+
uses: softprops/action-gh-release@3bb12739c298aeb8a4eeaf626c5b8d85266b0e65 # v2
6464
with:
6565
tag_name: ${{ steps.version.outputs.tag }}
6666
name: "Beta ${{ steps.version.outputs.version }}"

.github/workflows/ci-java.yml

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,21 +10,54 @@ jobs:
1010
build:
1111
runs-on: ubuntu-latest
1212
steps:
13-
- uses: actions/checkout@v4
13+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4.2.2
1414
with:
1515
fetch-depth: 0
16-
- uses: actions/setup-java@v4
16+
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v4.7.1
1717
with:
1818
distribution: 'temurin'
1919
java-version: '25'
2020
cache: 'maven'
21-
- run: mvn clean verify -B
22-
- uses: actions/upload-artifact@v4
21+
# Cache the OWASP Dependency-Check NVD data directory across runs so the
22+
# CVE gate does not need to re-download the full feed on every PR.
23+
# `key` is unique per run (forces a save on every run), `restore-keys`
24+
# falls back to the most recent prior cache so the H2 DB is incrementally
25+
# updated rather than rebuilt.
26+
- uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
27+
with:
28+
path: ~/.m2/repository/org/owasp/dependency-check-data
29+
key: dependency-check-${{ runner.os }}-${{ github.run_id }}
30+
restore-keys: |
31+
dependency-check-${{ runner.os }}-
32+
# Pre-warm the OWASP Dependency-Check NVD cache as a SEPARATE Maven
33+
# invocation. On a cold cache (first run on a branch / cache eviction)
34+
# running `update-only` first avoids the dependency-check-maven 12.2.0
35+
# H2 init race that surfaces as `NullPointerException: Cannot invoke
36+
# BasicDataSource.getConnection() because connectionPool is null`
37+
# during the verify phase (observed on PR #74 build run 24930518462).
38+
# When the cache is warm this step short-circuits via the H2 incremental
39+
# update path. `failOnError=false` so a transient NVD-feed problem here
40+
# does not mask the real CVSS>=7 gate enforced in the verify step
41+
# below — that step still hard-fails on operational scanner failures
42+
# (Reviewer round-3 finding #1).
43+
- name: Pre-warm dependency-check NVD cache
44+
env:
45+
NVD_API_KEY: ${{ secrets.NVD_API_KEY }}
46+
run: mvn -B -ntp dependency-check:update-only -DfailOnError=false
47+
- name: Build + verify (jacoco 85% + SpotBugs + dependency-check)
48+
env:
49+
# When the NVD_API_KEY secret is unset, dependency-check falls back
50+
# to the unauthenticated NVD endpoint (rate-limited but functional
51+
# once the cache is warm). Provisioning the secret is tracked under
52+
# RAN-42.
53+
NVD_API_KEY: ${{ secrets.NVD_API_KEY }}
54+
run: mvn -B -ntp clean verify
55+
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v4.6.2
2356
if: always()
2457
with:
2558
name: test-results
2659
path: target/surefire-reports/
27-
- uses: actions/upload-artifact@v4
60+
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v4.6.2
2861
with:
2962
name: coverage-report
3063
path: target/site/jacoco/

.github/workflows/release-java.yml

Lines changed: 66 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ jobs:
1212
permissions:
1313
contents: write
1414
steps:
15-
- uses: actions/checkout@v4
16-
- uses: actions/setup-java@v4
15+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4.2.2
16+
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v4.7.1
1717
with:
1818
distribution: 'temurin'
1919
java-version: '25'
@@ -23,23 +23,80 @@ jobs:
2323
server-password: MAVEN_PASSWORD
2424
gpg-private-key: ${{ secrets.MAVEN_GPG_PRIVATE_KEY }}
2525
gpg-passphrase: MAVEN_GPG_PASSPHRASE
26-
- name: Set release version
26+
- name: Configure git identity and non-interactive GPG for signed commit/tag
27+
run: |
28+
git config user.email "github-actions[bot]@users.noreply.github.com"
29+
git config user.name "github-actions[bot]"
30+
# Use the GPG key imported by setup-java (MAVEN_GPG_PRIVATE_KEY) for
31+
# both commit and tag signing — same trust path as the artifact.
32+
KEYID=$(gpg --list-secret-keys --with-colons | awk -F: '/^sec:/ {print $5; exit}')
33+
if [ -z "$KEYID" ]; then
34+
echo "no GPG secret key in agent — release-java.yml needs MAVEN_GPG_PRIVATE_KEY" >&2
35+
exit 1
36+
fi
37+
git config user.signingkey "$KEYID"
38+
git config gpg.format openpgp
39+
git config commit.gpgsign true
40+
git config tag.gpgsign true
41+
# Reviewer finding cf64b44d (RAN-47, R5-1):
42+
# `git commit -S` / `git tag -s` invoke gpg interactively by default
43+
# and fail in non-interactive Actions shells when the imported key
44+
# has a passphrase. setup-java only wires the passphrase for Maven
45+
# signing (via MAVEN_GPG_PASSPHRASE in settings.xml); git itself
46+
# has no equivalent autoconfig. Configure the gpg-agent for loopback
47+
# pinentry, point gpg.program at a thin wrapper that injects
48+
# --batch / --pinentry-mode loopback / --passphrase from
49+
# MAVEN_GPG_PASSPHRASE, so signing succeeds non-interactively.
50+
mkdir -p "$HOME/.gnupg"
51+
chmod 700 "$HOME/.gnupg"
52+
printf '%s\n' 'use-agent' 'pinentry-mode loopback' > "$HOME/.gnupg/gpg.conf"
53+
printf '%s\n' 'allow-loopback-pinentry' > "$HOME/.gnupg/gpg-agent.conf"
54+
gpgconf --kill gpg-agent || true
55+
# Wrapper script: git invokes this with the same flags as gpg.
56+
# We exec into gpg with --batch + loopback + the passphrase from
57+
# the env (MAVEN_GPG_PASSPHRASE is set on each step that signs).
58+
cat > "$HOME/.gnupg/gpg-loopback.sh" <<'WRAPPER'
59+
#!/usr/bin/env bash
60+
# Non-interactive gpg wrapper for `git commit -S` / `git tag -s`.
61+
# MAVEN_GPG_PASSPHRASE is set on the workflow step that signs.
62+
exec gpg --batch --yes --pinentry-mode loopback \
63+
--passphrase "${MAVEN_GPG_PASSPHRASE:-}" "$@"
64+
WRAPPER
65+
chmod +x "$HOME/.gnupg/gpg-loopback.sh"
66+
git config gpg.program "$HOME/.gnupg/gpg-loopback.sh"
67+
- name: Set release version and create signed release commit
2768
env:
2869
RELEASE_VERSION: ${{ inputs.version }}
29-
run: mvn versions:set -DnewVersion="$RELEASE_VERSION"
70+
# Picked up by the gpg-loopback wrapper script for `git commit -S`.
71+
MAVEN_GPG_PASSPHRASE: ${{ secrets.MAVEN_GPG_PASSPHRASE }}
72+
# Commit the version bump on a detached HEAD off the workflow's
73+
# checkout. The commit is reachable only via the tag created below —
74+
# no push to `main`, so this works under branch protection.
75+
# The commit captures the exact source tree that the deploy step
76+
# will build from, fixing the prior "tag diverges from released
77+
# artifact" gap (Reviewer finding 47b718b9).
78+
run: |
79+
mvn -B -ntp versions:set -DnewVersion="$RELEASE_VERSION" -DgenerateBackupPoms=false
80+
git add pom.xml
81+
git commit -S -m "chore(release): ${RELEASE_VERSION}"
3082
- name: Deploy to Maven Central
3183
env:
3284
MAVEN_USERNAME: ${{ secrets.OSS_NEXUS_USER }}
3385
MAVEN_PASSWORD: ${{ secrets.OSS_NEXUS_PASS }}
3486
MAVEN_GPG_PASSPHRASE: ${{ secrets.MAVEN_GPG_PASSPHRASE }}
35-
run: mvn clean deploy -P release -B
36-
- name: Tag release
87+
run: mvn -B -ntp -P release clean deploy
88+
- name: Create signed annotated tag and push
3789
env:
3890
RELEASE_VERSION: ${{ inputs.version }}
91+
MAVEN_GPG_PASSPHRASE: ${{ secrets.MAVEN_GPG_PASSPHRASE }}
92+
# Annotated + GPG-signed tag pointing at the release commit (the
93+
# current HEAD after the commit step above). Push only the tag —
94+
# the release commit lives only as a tag-reachable object so we
95+
# never need to update `main`, and branch protection stays clean.
3996
run: |
40-
git tag "v${RELEASE_VERSION}"
41-
git push origin "v${RELEASE_VERSION}"
42-
- uses: softprops/action-gh-release@v2
97+
git tag -s "v${RELEASE_VERSION}" -m "codeiq ${RELEASE_VERSION}"
98+
git push origin "refs/tags/v${RELEASE_VERSION}"
99+
- uses: softprops/action-gh-release@3bb12739c298aeb8a4eeaf626c5b8d85266b0e65 # v2
43100
with:
44101
tag_name: v${{ inputs.version }}
45102
generate_release_notes: true

0 commit comments

Comments
 (0)