Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 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
28 changes: 23 additions & 5 deletions .github/workflows/ci-java.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,39 @@ 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 }}-
- 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
48 changes: 39 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,53 @@ 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 for signed release commit and 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
- name: Set release version and create signed release commit
env:
RELEASE_VERSION: ${{ inputs.version }}
run: mvn versions:set -DnewVersion="$RELEASE_VERSION"
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
66 changes: 66 additions & 0 deletions .github/workflows/scorecard.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# OpenSSF Scorecard supply-chain analysis.
# RAN-46 AC #9. Best-effort target — no hard numeric floor; Scorecard does not gate merge.
# Docs: https://github.com/ossf/scorecard-action

name: Scorecard supply-chain security

on:
push:
branches: [main]
schedule:
# Mondays 06:00 UTC
- cron: "0 6 * * 1"
workflow_dispatch:

# Restrict the default GITHUB_TOKEN to read-only; the steps below request the
# narrow scopes they actually need.
permissions: read-all

jobs:
analysis:
name: Scorecard analysis
runs-on: ubuntu-latest
permissions:
# Required for upload to the code-scanning Security tab.
security-events: write
# Required to read OIDC token for publish_results.
id-token: write
# Default scopes for actions/checkout.
contents: read
actions: read

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: Run Scorecard analysis
# ossf/scorecard-action v2.4.3
uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a
with:
results_file: results.sarif
results_format: sarif
# Publish the results so they appear on the public Scorecard dashboard.
publish_results: true

- name: Upload Scorecard SARIF (artifact)
# actions/upload-artifact v7.0.1
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a
with:
name: scorecard-sarif
path: results.sarif
retention-days: 5

- name: Upload SARIF to GitHub code-scanning
# github/codeql-action/upload-sarif v3.35.2
uses: github/codeql-action/upload-sarif@ce64ddcb0d8d890d2df4a9d1c04ff297367dea2a
with:
sarif_file: results.sarif
Loading
Loading