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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions .bestpractices.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{
"$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,
"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",
"repo_url": "https://github.com/RandomCodeSpace/codeiq",
"license": "MIT",
"level": "passing",
"status": {
"basics": "self-assessed-passing",
"change_control": "self-assessed-passing",
"reporting": "self-assessed-passing",
"quality": "self-assessed-passing",
"security": "self-assessed-passing",
"analysis": "self-assessed-passing"
},
"evidence": {
"vulnerability_report_process": "SECURITY.md",
"release_process": "shared/runbooks/release.md",
"rollback_process": "shared/runbooks/rollback.md",
"first_time_setup": "shared/runbooks/first-time-setup.md",
"engineering_standards": "shared/runbooks/engineering-standards.md",
"license_file": "LICENSE",
"build_reproducible": "mvn -B -ntp clean verify",
"ci_workflow": ".github/workflows/ci-java.yml",
"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.",
"supply_chain_scorecard": ".github/workflows/scorecard.yml",
"dependency_updates": ".github/dependabot.yml",
"signed_commits": "scripts/setup-git-signed.sh",
"secret_scanning": "GitHub repo setting (secret_scanning + push_protection enabled)",
"static_analysis": "SpotBugs (mvn spotbugs:check) + SonarCloud Quality Gate",
"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."
}
}
125 changes: 125 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
# Dependabot configuration for codeiq.
# Docs: https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
#
# Strategy:
# * weekly cadence — keeps the noise floor low while still catching CVEs early
# * grouped updates per ecosystem so PR fan-out stays manageable
# * security updates fire whenever needed regardless of the weekly slot
#
# RAN-46 AC #4: Dependabot (security + version updates, weekly, grouped). Also
# enable repo-level "Dependabot security updates" via gh api (the version-updates
# below cover routine bumps; security updates are the reactive channel).

version: 2
updates:
# ----- Maven (the codeiq application) -----
- package-ecosystem: "maven"
directory: "/"
schedule:
interval: "weekly"
day: "monday"
time: "08:00"
timezone: "Etc/UTC"
open-pull-requests-limit: 10
labels:
- "type:dependencies"
- "area:backend"
commit-message:
prefix: "chore(deps)"
include: "scope"
groups:
spring:
patterns:
- "org.springframework*"
- "org.springframework.boot:*"
- "org.springframework.security:*"
- "org.springframework.ai:*"
jackson:
patterns:
- "com.fasterxml.jackson*"
neo4j:
patterns:
- "org.neo4j:*"
- "org.neo4j.driver:*"
antlr:
patterns:
- "org.antlr:*"
maven-plugins:
patterns:
- "org.apache.maven.plugins:*"
- "org.codehaus.*"
- "org.jacoco:*"
- "com.github.spotbugs:*"
- "org.owasp:*"
- "org.sonarsource.scanner.maven:*"
- "org.sonatype.central:*"
test-libs:
patterns:
- "org.junit.jupiter:*"
- "org.mockito:*"
- "org.assertj:*"
- "org.hamcrest:*"
- "com.h2database:*"

# ----- GitHub Actions (CI / release / security) -----
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
day: "monday"
time: "08:00"
timezone: "Etc/UTC"
open-pull-requests-limit: 5
labels:
- "type:dependencies"
- "area:ci"
commit-message:
prefix: "chore(actions)"
include: "scope"
groups:
actions:
patterns:
- "*"

# ----- Frontend (npm under src/main/frontend) -----
- package-ecosystem: "npm"
directory: "/src/main/frontend"
schedule:
interval: "weekly"
day: "monday"
time: "08:00"
timezone: "Etc/UTC"
open-pull-requests-limit: 5
labels:
- "type:dependencies"
- "area:frontend"
commit-message:
prefix: "chore(frontend)"
include: "scope"
groups:
react:
patterns:
- "react"
- "react-dom"
- "@types/react*"
ant-design:
patterns:
- "antd"
- "@ant-design/*"
vite:
patterns:
- "vite"
- "@vitejs/*"
echarts:
patterns:
- "echarts"
- "echarts-for-react"
eslint:
patterns:
- "eslint*"
- "@eslint/*"
- "@typescript-eslint/*"
typescript:
patterns:
- "typescript"
- "@types/*"
6 changes: 3 additions & 3 deletions .github/workflows/beta-java.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ jobs:
contents: write
packages: write
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4.2.2
with:
fetch-depth: 0

- uses: actions/setup-java@v4
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v4.7.1
with:
distribution: 'temurin'
java-version: '25'
Expand Down Expand Up @@ -60,7 +60,7 @@ jobs:
git push origin ${{ steps.version.outputs.tag }}

- name: Create GitHub Release
uses: softprops/action-gh-release@v2
uses: softprops/action-gh-release@3bb12739c298aeb8a4eeaf626c5b8d85266b0e65 # v2
with:
tag_name: ${{ steps.version.outputs.tag }}
name: "Beta ${{ steps.version.outputs.version }}"
Expand Down
43 changes: 38 additions & 5 deletions .github/workflows/ci-java.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,54 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4.2.2
with:
fetch-depth: 0
- uses: actions/setup-java@v4
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v4.7.1
with:
distribution: 'temurin'
java-version: '25'
cache: 'maven'
- run: mvn clean verify -B
- uses: actions/upload-artifact@v4
# Cache the OWASP Dependency-Check NVD data directory across runs so the
# CVE gate does not need to re-download the full feed on every PR.
# `key` is unique per run (forces a save on every run), `restore-keys`
# falls back to the most recent prior cache so the H2 DB is incrementally
# updated rather than rebuilt.
- uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: ~/.m2/repository/org/owasp/dependency-check-data
key: dependency-check-${{ runner.os }}-${{ github.run_id }}
restore-keys: |
dependency-check-${{ runner.os }}-
# Pre-warm the OWASP Dependency-Check NVD cache as a SEPARATE Maven
# invocation. On a cold cache (first run on a branch / cache eviction)
# running `update-only` first avoids the dependency-check-maven 12.2.0
# H2 init race that surfaces as `NullPointerException: Cannot invoke
# BasicDataSource.getConnection() because connectionPool is null`
# during the verify phase (observed on PR #74 build run 24930518462).
# When the cache is warm this step short-circuits via the H2 incremental
# update path. `failOnError=false` so a transient NVD-feed problem here
# does not mask the real CVSS>=7 gate enforced in the verify step
# below — that step still hard-fails on operational scanner failures
# (Reviewer round-3 finding #1).
- name: Pre-warm dependency-check NVD cache
env:
NVD_API_KEY: ${{ secrets.NVD_API_KEY }}
run: mvn -B -ntp dependency-check:update-only -DfailOnError=false
- name: Build + verify (jacoco 85% + SpotBugs + dependency-check)
env:
# When the NVD_API_KEY secret is unset, dependency-check falls back
# to the unauthenticated NVD endpoint (rate-limited but functional
# once the cache is warm). Provisioning the secret is tracked under
# RAN-42.
NVD_API_KEY: ${{ secrets.NVD_API_KEY }}
run: mvn -B -ntp clean verify
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Run SpotBugs in CI rather than relying on verify phase

This workflow only executes mvn -B -ntp clean verify, but spotbugs-maven-plugin is not bound to any lifecycle phase in pom.xml, so spotbugs:check is never executed here. In practice, PRs can merge with new High/Critical SpotBugs findings even though this step and the runbooks describe SpotBugs as part of the blocking gate.

Useful? React with 👍 / 👎.

- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v4.6.2
if: always()
with:
name: test-results
path: target/surefire-reports/
- uses: actions/upload-artifact@v4
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v4.6.2
with:
name: coverage-report
path: target/site/jacoco/
Expand Down
75 changes: 66 additions & 9 deletions .github/workflows/release-java.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ jobs:
permissions:
contents: write
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4.2.2
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v4.7.1
with:
distribution: 'temurin'
java-version: '25'
Expand All @@ -23,23 +23,80 @@ jobs:
server-password: MAVEN_PASSWORD
gpg-private-key: ${{ secrets.MAVEN_GPG_PRIVATE_KEY }}
gpg-passphrase: MAVEN_GPG_PASSPHRASE
- name: Set release version
- name: Configure git identity and non-interactive GPG for signed commit/tag
run: |
git config user.email "github-actions[bot]@users.noreply.github.com"
git config user.name "github-actions[bot]"
# Use the GPG key imported by setup-java (MAVEN_GPG_PRIVATE_KEY) for
# both commit and tag signing — same trust path as the artifact.
KEYID=$(gpg --list-secret-keys --with-colons | awk -F: '/^sec:/ {print $5; exit}')
if [ -z "$KEYID" ]; then
echo "no GPG secret key in agent — release-java.yml needs MAVEN_GPG_PRIVATE_KEY" >&2
exit 1
fi
git config user.signingkey "$KEYID"
git config gpg.format openpgp
git config commit.gpgsign true
git config tag.gpgsign true
# Reviewer finding cf64b44d (RAN-47, R5-1):
# `git commit -S` / `git tag -s` invoke gpg interactively by default
# and fail in non-interactive Actions shells when the imported key
# has a passphrase. setup-java only wires the passphrase for Maven
# signing (via MAVEN_GPG_PASSPHRASE in settings.xml); git itself
# has no equivalent autoconfig. Configure the gpg-agent for loopback
# pinentry, point gpg.program at a thin wrapper that injects
# --batch / --pinentry-mode loopback / --passphrase from
# MAVEN_GPG_PASSPHRASE, so signing succeeds non-interactively.
mkdir -p "$HOME/.gnupg"
chmod 700 "$HOME/.gnupg"
printf '%s\n' 'use-agent' 'pinentry-mode loopback' > "$HOME/.gnupg/gpg.conf"
printf '%s\n' 'allow-loopback-pinentry' > "$HOME/.gnupg/gpg-agent.conf"
gpgconf --kill gpg-agent || true
# Wrapper script: git invokes this with the same flags as gpg.
# We exec into gpg with --batch + loopback + the passphrase from
# the env (MAVEN_GPG_PASSPHRASE is set on each step that signs).
cat > "$HOME/.gnupg/gpg-loopback.sh" <<'WRAPPER'
#!/usr/bin/env bash
# Non-interactive gpg wrapper for `git commit -S` / `git tag -s`.
# MAVEN_GPG_PASSPHRASE is set on the workflow step that signs.
exec gpg --batch --yes --pinentry-mode loopback \
--passphrase "${MAVEN_GPG_PASSPHRASE:-}" "$@"
WRAPPER
chmod +x "$HOME/.gnupg/gpg-loopback.sh"
git config gpg.program "$HOME/.gnupg/gpg-loopback.sh"
- name: Set release version and create signed release commit
env:
RELEASE_VERSION: ${{ inputs.version }}
run: mvn versions:set -DnewVersion="$RELEASE_VERSION"
# Picked up by the gpg-loopback wrapper script for `git commit -S`.
MAVEN_GPG_PASSPHRASE: ${{ secrets.MAVEN_GPG_PASSPHRASE }}
# Commit the version bump on a detached HEAD off the workflow's
# checkout. The commit is reachable only via the tag created below —
# no push to `main`, so this works under branch protection.
# The commit captures the exact source tree that the deploy step
# will build from, fixing the prior "tag diverges from released
# artifact" gap (Reviewer finding 47b718b9).
run: |
mvn -B -ntp versions:set -DnewVersion="$RELEASE_VERSION" -DgenerateBackupPoms=false
git add pom.xml
git commit -S -m "chore(release): ${RELEASE_VERSION}"
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Make release commit signing non-interactive

The release workflow signs the version-bump commit with git commit -S, but this runs in a non-interactive GitHub Actions shell and does not consume MAVEN_GPG_PASSPHRASE; when MAVEN_GPG_PRIVATE_KEY is passphrase-protected (the normal case implied by this workflow’s secrets), signing blocks on pinentry and fails before deploy/tagging. This makes release-java.yml fail for protected keys unless you explicitly configure loopback/preset passphrase handling for git signing.

Useful? React with 👍 / 👎.

- name: Deploy to Maven Central
env:
MAVEN_USERNAME: ${{ secrets.OSS_NEXUS_USER }}
MAVEN_PASSWORD: ${{ secrets.OSS_NEXUS_PASS }}
MAVEN_GPG_PASSPHRASE: ${{ secrets.MAVEN_GPG_PASSPHRASE }}
run: mvn clean deploy -P release -B
- name: Tag release
run: mvn -B -ntp -P release clean deploy
- name: Create signed annotated tag and push
env:
RELEASE_VERSION: ${{ inputs.version }}
MAVEN_GPG_PASSPHRASE: ${{ secrets.MAVEN_GPG_PASSPHRASE }}
# Annotated + GPG-signed tag pointing at the release commit (the
# current HEAD after the commit step above). Push only the tag —
# the release commit lives only as a tag-reachable object so we
# never need to update `main`, and branch protection stays clean.
run: |
git tag "v${RELEASE_VERSION}"
git push origin "v${RELEASE_VERSION}"
- uses: softprops/action-gh-release@v2
git tag -s "v${RELEASE_VERSION}" -m "codeiq ${RELEASE_VERSION}"
git push origin "refs/tags/v${RELEASE_VERSION}"
- uses: softprops/action-gh-release@3bb12739c298aeb8a4eeaf626c5b8d85266b0e65 # v2
with:
tag_name: v${{ inputs.version }}
generate_release_notes: true
Expand Down
Loading
Loading