Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions .bestpractices.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"$schema": "https://bestpractices.coreinfrastructure.org/projects.schema.json",
"_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.",
"project_id": null,
"_comment": "OpenSSF Best Practices self-assessment for RandomCodeSpace/codeiq. Project page: https://www.bestpractices.dev/en/projects/12650. RAN-46 AC #8 (registration) was unblocked by the board between RAN-46 and RAN-52 — project_id is now wired and the README badge points at the live project URL. Flipping `badge_level` from `in_progress` to `passing` happens in the bestpractices.dev admin UI (still board-owned).",
"project_id": 12650,
"name": "codeiq",
"description": "Deterministic code knowledge graph — scans codebases to map services, endpoints, entities, infrastructure, auth patterns, and framework usage. No AI, pure static analysis.",
"homepage_url": "https://github.com/RandomCodeSpace/codeiq",
Expand Down Expand Up @@ -34,8 +34,9 @@
"vulnerability_scanning": "OWASP Dependency-Check (mvn dependency-check:check) + Dependabot security updates"
},
"audit": {
"self_assessment_date": "2026-04-25",
"self_assessment_author": "TechLead (RAN-46)",
"registration_blocker": "https://www.bestpractices.dev/ requires human OAuth/form. Tracked under RAN-46 AC #8."
"self_assessment_date": "2026-04-26",
"self_assessment_author": "TechLead (RAN-46, RAN-52)",
"registration_blocker": null,
"passing_blocker": "Final flip from `in_progress` to `passing` happens in the bestpractices.dev admin UI; tracked under RAN-52 AC #1 + AC #2."
}
}
330 changes: 330 additions & 0 deletions .github/workflows/security.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,330 @@
# Consolidated OSS-CLI security stack (RAN-52 AC #4).
#
# Mirrors the (B) OSS-CLI security stack referenced in
# `shared/runbooks/engineering-standards.md` §5: Semgrep (SAST), OSV-Scanner
# (deps), Trivy (filesystem CVEs + misconfig), Gitleaks (secret scan), jscpd
# (copy-paste detection), and anchore/sbom-action (SBOM generation).
#
# Each tool publishes its findings as SARIF to GitHub code scanning where
# supported and uploads the raw report as a workflow artifact regardless,
# so the Security tab plus the artifact tarball are both first-class
# observability surfaces. SARIF upload jobs use `continue-on-error: true`
# during the OSS-CLI bootstrap window because a fresh repo still has the
# default-setup CodeQL bound to some categories — once a category is taken
# by default setup GitHub rejects the workflow upload (same trap that bit
# `codeql.yml` in PR #74). Findings still appear in the workflow logs and
# artifacts, and we promote them to gate-blocking once category collisions
# are settled (tracked under RAN-52 follow-ups).
#
# Cron: Mondays 06:00 UTC, same window as `scorecard.yml` so the weekly
# observability sweep runs together. All third-party actions and Docker
# images are pinned by commit SHA / digest-equivalent tag, in line with
# OpenSSF Scorecard `Pinned-Dependencies`.
name: Security supply-chain scan

on:
push:
branches: [main]
pull_request:
branches: [main]
schedule:
- cron: "0 6 * * 1"
workflow_dispatch:

# Restrict default GITHUB_TOKEN to read-only; jobs opt into narrower writes.
permissions: read-all

jobs:
# ------------------------------------------------------------------
# Semgrep — SAST. Runs the official `semgrep` PyPI distribution rather
# than the deprecated `semgrep/semgrep-action` (last release 2023). The
# `p/default` registry pack covers Java, TypeScript, JS, YAML, Dockerfile,
# GHA, and a `security-audit` ruleset, which matches codeiq's tree.
# ------------------------------------------------------------------
semgrep:
name: Semgrep SAST
runs-on: ubuntu-latest
permissions:
contents: read
security-events: write
steps:
- name: Harden runner egress
# step-security/harden-runner v2.19.0
uses: step-security/harden-runner@8d3c67de8e2fe68ef647c8db1e6a09f647780f40
with:
egress-policy: audit

- name: Checkout code
# actions/checkout v6.0.2
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
with:
persist-credentials: false

- name: Set up Python
# actions/setup-python v5
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065
with:
python-version: "3.12"

- name: Install Semgrep
run: python -m pip install --no-cache-dir 'semgrep==1.140.0'

- name: Run Semgrep
# `--error` would fail the job; we report-only at bootstrap and
# gate later. SARIF goes to code scanning, JSON to artifact.
run: |
semgrep scan \
--config=p/default \
--config=p/security-audit \
--sarif --output=semgrep.sarif \
--metrics=off || true

- name: Upload Semgrep SARIF (artifact)
# actions/upload-artifact v7.0.1
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a
if: always()
with:
name: semgrep-sarif
path: semgrep.sarif
retention-days: 7

- name: Upload Semgrep SARIF to GitHub code-scanning
# github/codeql-action/upload-sarif v3.35.2
uses: github/codeql-action/upload-sarif@ce64ddcb0d8d890d2df4a9d1c04ff297367dea2a
if: always()
continue-on-error: true
with:
sarif_file: semgrep.sarif
category: semgrep

# ------------------------------------------------------------------
# OSV-Scanner — second-source CVE feed (cross-checks OWASP
# Dependency-Check from `ci-java.yml`). Fulfils the previously
# planned RAN-42 osv-scanner.yml; engineering-standards.md §5 row
# is updated in this same PR.
# ------------------------------------------------------------------
osv-scanner:
name: OSV-Scanner deps
runs-on: ubuntu-latest
permissions:
contents: read
security-events: write
steps:
- name: Harden runner egress
uses: step-security/harden-runner@8d3c67de8e2fe68ef647c8db1e6a09f647780f40
with:
egress-policy: audit

- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
with:
persist-credentials: false

- name: Run OSV-Scanner
# google/osv-scanner-action v2.3.5
uses: google/osv-scanner-action@c51854704019a247608d928f370c98740469d4b5
with:
scan-args: |-
--recursive
--skip-git
--format=sarif
--output=osv.sarif
./
continue-on-error: true

- name: Upload OSV SARIF (artifact)
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a
if: always()
with:
name: osv-sarif
path: osv.sarif
retention-days: 7

- name: Upload OSV SARIF to GitHub code-scanning
uses: github/codeql-action/upload-sarif@ce64ddcb0d8d890d2df4a9d1c04ff297367dea2a
if: always()
continue-on-error: true
with:
sarif_file: osv.sarif
category: osv-scanner

# ------------------------------------------------------------------
# Trivy — filesystem CVE + IaC misconfig scan. We scan `.` rather
# than a container image because codeiq does not ship images yet
# (see CLAUDE.md "Deploy" section).
# ------------------------------------------------------------------
trivy-fs:
name: Trivy filesystem
runs-on: ubuntu-latest
permissions:
contents: read
security-events: write
steps:
- name: Harden runner egress
uses: step-security/harden-runner@8d3c67de8e2fe68ef647c8db1e6a09f647780f40
with:
egress-policy: audit

- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
with:
persist-credentials: false

- name: Run Trivy filesystem scan
# aquasecurity/trivy-action v0.36.0
uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25
with:
scan-type: fs
scan-ref: .
format: sarif
output: trivy.sarif
ignore-unfixed: true
severity: CRITICAL,HIGH
exit-code: "0"
env:
# Mirror the public DB; harden-runner audits egress.
TRIVY_DISABLE_VEX_NOTICE: "true"

- name: Upload Trivy SARIF (artifact)
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a
if: always()
with:
name: trivy-sarif
path: trivy.sarif
retention-days: 7

- name: Upload Trivy SARIF to GitHub code-scanning
uses: github/codeql-action/upload-sarif@ce64ddcb0d8d890d2df4a9d1c04ff297367dea2a
if: always()
continue-on-error: true
with:
sarif_file: trivy.sarif
category: trivy-fs

# ------------------------------------------------------------------
# Gitleaks — secret scan. Run via the upstream Docker image
# rather than the GHA wrapper because gitleaks-action requires a
# GITLEAKS_LICENSE for organization-owned repos (RandomCodeSpace
# is an org). Image is pinned by tag; image digest pinning is a
# follow-up once Scorecard `Pinned-Dependencies` flags it.
# ------------------------------------------------------------------
gitleaks:
name: Gitleaks secret scan
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Harden runner egress
uses: step-security/harden-runner@8d3c67de8e2fe68ef647c8db1e6a09f647780f40
with:
egress-policy: audit

- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
with:
# Need full history so gitleaks can scan every commit.
fetch-depth: 0
persist-credentials: false

- name: Run Gitleaks (Docker image)
run: |
docker run --rm \
-v "${{ github.workspace }}":/repo \
-w /repo \
zricethezav/gitleaks:v8.21.2 \
detect \
--source=/repo \
--report-format=sarif \
--report-path=/repo/gitleaks.sarif \
--redact \
--no-banner || EXIT=$?
# Exit 1 = leaks found; 2 = error. Surface the result without
# gating CI yet — promote to gate once we have a clean baseline.
echo "gitleaks-exit=${EXIT:-0}" >> "$GITHUB_OUTPUT"
id: gitleaks-run

- name: Upload Gitleaks SARIF (artifact)
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a
if: always()
with:
name: gitleaks-sarif
path: gitleaks.sarif
retention-days: 7

# ------------------------------------------------------------------
# jscpd — copy-paste detection across the polyglot tree. Reports
# are uploaded as artifacts; jscpd does not emit SARIF natively
# so we surface markdown + json in artifacts only.
# ------------------------------------------------------------------
jscpd:
name: jscpd duplication
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Harden runner egress
uses: step-security/harden-runner@8d3c67de8e2fe68ef647c8db1e6a09f647780f40
with:
egress-policy: audit

- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
with:
persist-credentials: false

- name: Set up Node
# actions/setup-node v4
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020
with:
node-version: "20"

- name: Run jscpd
run: |
npx --yes jscpd@4.0.5 \
--silent \
--reporters json,markdown \
--output reports/jscpd \
--ignore '**/node_modules/**,**/target/**,**/dist/**,**/*.min.*,src/main/java/io/github/randomcodespace/iq/grammar/**,src/main/antlr4/imported/**' \
. || true

- name: Upload jscpd report (artifact)
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a
if: always()
with:
name: jscpd-report
path: reports/jscpd
retention-days: 7

# ------------------------------------------------------------------
# SBOM — Syft via anchore/sbom-action. Generates SPDX JSON for
# the source tree and attaches it to the run; sbom-action also
# publishes it to the dependency-graph endpoint when run on
# push to default branch.
# ------------------------------------------------------------------
sbom:
name: Generate SBOM (Syft)
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Harden runner egress
uses: step-security/harden-runner@8d3c67de8e2fe68ef647c8db1e6a09f647780f40
with:
egress-policy: audit

- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
with:
persist-credentials: false

- name: Generate SBOM
# anchore/sbom-action v0.24.0
uses: anchore/sbom-action@e22c389904149dbc22b58101806040fa8d37a610
with:
path: .
format: spdx-json
artifact-name: codeiq.spdx.json
# `dependency-snapshot: true` would push to the GitHub
# Dependency Submission API; we keep it false for PRs and
# let the scheduled run on `main` populate the graph.
dependency-snapshot: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
21 changes: 21 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,27 @@ bean for code paths that haven't been ported yet.
- **SonarCloud project key**: `RandomCodeSpace_codeiq`, org: `randomcodespace`
- **CI workflow**: Single `ci-java.yml` runs build + SonarCloud analysis. No cross-platform builds needed (JVM).

## Supply-chain observability (OpenSSF)

codeiq publishes two OpenSSF signals: the **Best Practices** badge (a self-attested checklist) and the **Scorecard** score (an automated supply-chain audit).

### Best Practices badge

- Project: https://www.bestpractices.dev/en/projects/12650 — registration unblocked between RAN-46 and RAN-52.
- Source-of-truth manifest: `.bestpractices.json` at repo root (project_id, evidence map, audit dates).
- Hard gate per the board: badge level **`passing`**. Final flip from `in_progress` → `passing` happens in the bestpractices.dev admin UI (board-owned). The repo-side criteria (CHANGELOG, SECURITY.md, signed commits, CI, code scanning, vulnerability scanning, SBOM, Scorecard wiring, dependency updates) all already point to evidence in this repo.

### Scorecard baseline + target

- Workflow: [`.github/workflows/scorecard.yml`](.github/workflows/scorecard.yml) — push to `main`, weekly cron (Mondays 06:00 UTC), `workflow_dispatch`. SARIF goes to the GitHub Security tab; results also land on https://api.securityscorecards.dev/projects/github.com/RandomCodeSpace/codeiq.
- **Baseline (RAN-52 close, 2026-04-26):** captured by the first scheduled run after this PR lands. Read live from the Scorecard project page above; no static checked-in score (it would rot).
- **Target:** ≥ **8.0 / 10**, with these checks at max: `Pinned-Dependencies`, `Token-Permissions`, `Branch-Protection`, `Code-Review`, `Maintained`, `License`, `SAST`, `Vulnerabilities`. **Stretch only** — Scorecard is observational; the `passing` Best Practices badge is the only hard gate per the board.
- **Known floor reductions:** `Webhooks` (no public webhook surface — N/A), `Signed-Releases` (release-java workflow signs the GA commit; we are not yet signing every release artifact via Sigstore — tracked under follow-up).

### OSS-CLI security stack

The (B) OSS-CLI stack runs in [`.github/workflows/security.yml`](.github/workflows/security.yml): **Semgrep** (SAST), **OSV-Scanner** (deps, second-source CVE feed cross-checking OWASP Dependency-Check), **Trivy** (filesystem CVEs + IaC misconfig), **Gitleaks** (secret scan, Docker-image), **jscpd** (copy-paste detection), and **anchore/sbom-action** (SPDX SBOM). Triggers: push to `main`, PR, weekly cron, `workflow_dispatch`. SARIF outputs are uploaded to GitHub code scanning where supported, and every job uploads its raw report as a workflow artifact regardless. Findings are observability-only at the OSS-CLI bootstrap window — promote to gate-blocking once a clean baseline exists.

## Deploy

codeiq's deploy surface is **Maven Central + GitHub Releases** (per RAN-46 AC #10 ruling, option a). The single Java JAR (with the React UI bundled inside) is published via two `workflow_dispatch`-only workflows: `.github/workflows/beta-java.yml` (manual beta cut → Sonatype Central beta + GitHub pre-release) and `.github/workflows/release-java.yml` (manual GA cut with a `version` input → the workflow builds a GPG-signed release commit on a detached HEAD, deploys from that exact tree, then creates and pushes a GPG-signed annotated `vX.Y.Z` tag pointing at the release commit + a GitHub Release). There is no static-CDN frontend, no hosted backend, no VPS — codeiq runs on the developer's machine. See [`shared/runbooks/release.md`](shared/runbooks/release.md) and [`shared/runbooks/engineering-standards.md`](shared/runbooks/engineering-standards.md) §7.1.
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
<a href="https://sonarcloud.io/summary/overall?id=RandomCodeSpace_codeiq"><img src="https://sonarcloud.io/api/project_badges/measure?project=RandomCodeSpace_codeiq&metric=security_rating" alt="Security"></a>
<a href="https://sonarcloud.io/summary/overall?id=RandomCodeSpace_codeiq"><img src="https://sonarcloud.io/api/project_badges/measure?project=RandomCodeSpace_codeiq&metric=reliability_rating" alt="Reliability"></a>
<a href="https://api.securityscorecards.dev/projects/github.com/RandomCodeSpace/codeiq"><img src="https://api.securityscorecards.dev/projects/github.com/RandomCodeSpace/codeiq/badge" alt="OpenSSF Scorecard"></a>
<a href="https://www.bestpractices.dev/projects/codeiq"><img src="https://img.shields.io/badge/OpenSSF%20Best%20Practices-pending%20registration-lightgrey?style=flat-square&logo=openssf&logoColor=white" alt="OpenSSF Best Practices (pending registration — RAN-46 AC #8)"></a>
<a href="https://www.bestpractices.dev/projects/12650"><img src="https://www.bestpractices.dev/projects/12650/badge" alt="OpenSSF Best Practices"></a>
<a href="https://github.com/RandomCodeSpace/codeiq"><img src="https://img.shields.io/badge/detectors-97-brightgreen?style=flat-square&logo=codefactor&logoColor=white" alt="97 Detectors"></a>
<a href="https://github.com/RandomCodeSpace/codeiq"><img src="https://img.shields.io/badge/languages-35%2B-blue?style=flat-square&logo=stackblitz&logoColor=white" alt="35+ Languages"></a>
</p>
Expand Down
Loading
Loading