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
35 changes: 35 additions & 0 deletions .bestpractices.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,39 @@
{
"$schema": "https://bestpractices.coreinfrastructure.org/projects.schema.json",
"_comment": "OpenSSF Best Practices self-assessment for RandomCodeSpace/docsiq. Project is registered (id 12628) and the badge currently displays 'passing' — see https://www.bestpractices.dev/en/projects/12628. The audit_* fields below mirror the per-criterion answers submitted to the OpenSSF Best Practices site; refresh them whenever criteria scoring changes.",
"project_id": 12628,
"name": "docsiq",
"description": "GraphRAG-powered documentation search tool — indexes PDF/DOCX/TXT/MD/web content into a knowledge graph with entity extraction, community detection, and vector embeddings, then answers queries via graph + vector search.",
"homepage_url": "https://github.com/RandomCodeSpace/docsiq",
"repo_url": "https://github.com/RandomCodeSpace/docsiq",
"license": "MIT",
"level": "passing",
"badge_url": "https://www.bestpractices.dev/projects/12628/badge",
"project_page_url": "https://www.bestpractices.dev/en/projects/12628",
"evidence": {
"vulnerability_report_process": "SECURITY.md",
"license_file": "LICENSE",
"code_of_conduct": "CODE_OF_CONDUCT.md",
"contributing_guide": "CONTRIBUTING.md",
"governance": "GOVERNANCE.md",
"build_reproducible": "go build -tags sqlite_fts5 ./...",
"ci_workflow": ".github/workflows/ci.yml",
"code_scanning": ".github/workflows/codeql.yml",
"supply_chain_scorecard": ".github/workflows/scorecard.yml",
"oss_cli_security_stack": ".github/workflows/security.yml",
"fuzz_testing": ".github/workflows/fuzz.yml",
"dependency_updates": ".github/dependabot.yml",
"secret_scanning": "GitHub repo setting (secret_scanning + push_protection enabled)",
"private_vulnerability_reporting": "GitHub repo setting (security advisories enabled)",
"signed_releases": "cosign keyless signing via .github/workflows/release.yml + Sigstore Rekor"
},
"audit": {
"self_assessment_date": "2026-04-26",
"self_assessment_author": "TechLead (RAN-51)",
"ran_50_lane": "RAN-51 (recipe validation; replicates to otelcontext, snipIT, vigil)",
"scorecard_dashboard": "https://scorecard.dev/viewer/?uri=github.com/RandomCodeSpace/docsiq"
},

"description_good_status": "Met",
"description_good_justification": "See README.md. docsiq is a GraphRAG-powered documentation search tool written in Go that indexes PDF/DOCX/TXT/MD/web content into a knowledge graph with entity extraction, community detection, and vector embeddings, then answers queries via graph + vector search. https://github.com/RandomCodeSpace/docsiq/blob/main/README.md",

Expand Down
60 changes: 37 additions & 23 deletions .github/workflows/scorecard.yml
Original file line number Diff line number Diff line change
@@ -1,54 +1,68 @@
name: scorecard

# Triggers:
# - workflow_run on completed 'release' runs → scan fresh release assets
# - weekly schedule (Mondays, 06:00 UTC) → backstop against drift
# - branch_protection_rule changes → re-score when policy moves
# - manual workflow_dispatch → on-demand
# Not on every main push — most commits don't change release/scorecard-visible
# state, so we were burning runner time publishing stale results.
# OpenSSF Scorecard supply-chain analysis.
# RAN-51 hardening lane (mirrors codeiq RAN-46 recipe).
# Best-effort target — Scorecard does not gate merge; the OpenSSF Best
# Practices badge is the only hard gate per board.
# Docs: https://github.com/ossf/scorecard-action

name: Scorecard supply-chain security

on:
workflow_run:
workflows: [release]
types: [completed]
branch_protection_rule:
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
name: Scorecard analysis
runs-on: ubuntu-latest
permissions:
# Required to upload to the code-scanning Security tab.
security-events: write
# Required to read the OIDC token for publish_results.
id-token: write
# Default scopes for actions/checkout.
contents: read
actions: read

steps:
- name: checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- 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
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
with:
persist-credentials: false

- name: run scorecard
uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3
- name: Run Scorecard analysis
# ossf/scorecard-action v2.4.3
uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a
with:
results_file: results.sarif
results_format: sarif
# Publish so results appear on the public Scorecard dashboard.
publish_results: true

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

- name: upload sarif to code scanning
uses: github/codeql-action/upload-sarif@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4.35.2
- name: Upload SARIF to GitHub code-scanning
# github/codeql-action/upload-sarif v4.35.2
uses: github/codeql-action/upload-sarif@95e58e9a2cdfd71adc6e0353d5c52f41a045d225
with:
sarif_file: results.sarif
185 changes: 185 additions & 0 deletions .github/workflows/security.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
name: Security (OSS-CLI)
# OSS-CLI security stack — RAN-51 hardening lane.
# Mirrors the codeiq RAN-46 (B) OSS-CLI stack
# (Semgrep + osv-scanner + Trivy + Gitleaks + jscpd + sbom-action),
# adapted for docsiq's Go + React/TS shape.
#
# Six independent jobs — fail-fast off so every signal surfaces on a
# single run. All actions SHA-pinned per Scorecard `Pinned-Dependencies`.
# Top-level `permissions: read-all` per Scorecard `Token-Permissions`;
# jobs scope up only when needed (gitleaks needs full git history,
# the sbom job uploads artifacts).
on:
push:
branches: [main]
pull_request:
branches: [main]
schedule:
- cron: '21 4 * * 1' # Mondays 04:21 UTC — catch newly-disclosed CVEs

permissions: read-all

jobs:
osv-scanner:
name: OSV-Scanner (SCA)
runs-on: ubuntu-latest
permissions:
contents: read
env:
OSV_SCANNER_VERSION: 2.3.5
GH_TOKEN: ${{ github.token }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
persist-credentials: false
# Install osv-scanner from the official GitHub release (binary, not
# the action — google/osv-scanner-action's `action.yml` is composite
# and fails when invoked as a job step). Using the preinstalled
# `gh` CLI avoids any external `curl`/`wget`.
- name: Install osv-scanner
run: |
gh release download "v${OSV_SCANNER_VERSION}" \
--repo google/osv-scanner \
--pattern 'osv-scanner_linux_amd64' \
--clobber
mv osv-scanner_linux_amd64 osv-scanner
chmod +x osv-scanner
./osv-scanner --version
# Scan the Go module graph and the embedded React UI's npm lockfile.
# `--recursive` would also find ui/package-lock.json, but being
# explicit keeps the AC §3 ("Zero High/Critical CVEs") evidence
# trail clear: Go covered by go.mod, frontend by ui/package-lock.json,
# cross-ecosystem reactive coverage by Dependabot security updates.
- name: Run osv-scanner (Go modules)
run: ./osv-scanner --lockfile=go.mod
- name: Run osv-scanner (UI npm lockfile)
run: ./osv-scanner --lockfile=ui/package-lock.json

trivy:
name: Trivy (filesystem scan)
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
persist-credentials: false
- uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0
with:
scan-type: fs
scan-ref: .
severity: HIGH,CRITICAL
exit-code: '1'
ignore-unfixed: true

semgrep:
name: Semgrep (SAST)
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
persist-credentials: false
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: '3.12'
- name: Install semgrep
run: python -m pip install --quiet --upgrade pip semgrep
- name: Run semgrep (security-audit + owasp-top-ten + golang + typescript)
# `p/golang` covers Go-specific patterns (unsafe.Pointer, errcheck
# families, taint flows). `p/typescript` covers the embedded React
# UI under ui/. `p/security-audit` and `p/owasp-top-ten` are
# language-agnostic. `--metrics off` keeps runs offline-friendly.
run: |
semgrep scan \
--error \
--config p/security-audit \
--config p/owasp-top-ten \
--config p/golang \
--config p/typescript \
--severity ERROR \
--metrics off

gitleaks:
name: Gitleaks (secret scan)
runs-on: ubuntu-latest
permissions:
contents: read
env:
GITLEAKS_VERSION: 8.30.1
GH_TOKEN: ${{ github.token }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
fetch-depth: 0
persist-credentials: false
# The official `gitleaks/gitleaks-action` requires a paid licence for
# GitHub organisations. The underlying CLI is MIT-licensed; install
# it directly from the upstream release using the preinstalled `gh`.
- name: Install gitleaks
run: |
gh release download "v${GITLEAKS_VERSION}" \
--repo gitleaks/gitleaks \
--pattern "gitleaks_${GITLEAKS_VERSION}_linux_x64.tar.gz" \
--output gitleaks.tar.gz
tar -xzf gitleaks.tar.gz gitleaks
chmod +x gitleaks
- name: Run gitleaks (full git history)
run: ./gitleaks detect --source . --redact --no-banner --exit-code 1

jscpd:
name: jscpd (duplication < 3% on production code)
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
persist-credentials: false
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version: '20'
- name: Run jscpd
# Scope to production code only: cmd/ + internal/ (Go) and ui/src/
# (React/TS). Tests share fixture/assertion shape by design — that
# parallelism catches contract regressions and is not a refactor
# target. `--min-tokens 200` matches a meaningful method body, not
# language scaffolding (package + imports + struct headers).
run: |
npx --yes jscpd@4 \
--threshold 3 \
--min-tokens 200 \
--reporters consoleFull \
--format "go,javascript,typescript" \
--ignore "**/node_modules/**,**/dist/**,**/build/**,**/coverage/**,**/testdata/**,**/*_test.go,**/*.test.ts,**/*.spec.ts,**/e2e/**,**/.next/**,**/vendor/**" \
cmd internal ui/src

sbom:
name: SBOM (SPDX + CycloneDX)
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
persist-credentials: false
- name: Generate SPDX SBOM
uses: anchore/sbom-action@fc46e51fd3cb168ffb36c6d1915723c47db58abb # v0.17.7
with:
format: spdx-json
output-file: sbom.spdx.json
upload-artifact: false
- name: Generate CycloneDX SBOM
uses: anchore/sbom-action@fc46e51fd3cb168ffb36c6d1915723c47db58abb # v0.17.7
with:
format: cyclonedx-json
output-file: sbom.cdx.json
upload-artifact: false
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v4.6.2
with:
name: sbom
path: |
sbom.spdx.json
sbom.cdx.json
retention-days: 90
24 changes: 24 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,27 @@ internal/
- Error wrapping: `fmt.Errorf("context: %w", err)`
- Concurrency: use semaphore channels (`make(chan struct{}, N)`) for limiting parallelism
- Config: Viper with `mapstructure` tags, env prefix `DOCSIQ_`

## Security & Supply-Chain

docsiq is registered with the OpenSSF Best Practices programme as project
[12628](https://www.bestpractices.dev/en/projects/12628) and runs the OpenSSF
Scorecard alongside the OSS-CLI security stack on every push to `main` plus
weekly.

| Control | Source | Gate |
|---|---|---|
| OpenSSF Best Practices | [`.bestpractices.json`](.bestpractices.json) + project 12628 | **passing** badge — hard gate |
| OpenSSF Scorecard | [`.github/workflows/scorecard.yml`](.github/workflows/scorecard.yml) (push + weekly cron) | Observational, baseline ≥ current published score, stretch ≥ 8.0/10 — does **not** block merge |
| Semgrep / osv-scanner / Trivy / Gitleaks / jscpd / SBOM | [`.github/workflows/security.yml`](.github/workflows/security.yml) | High/Critical findings = block merge per `~/.claude/rules/security.md` |
| CodeQL | [`.github/workflows/codeql.yml`](.github/workflows/codeql.yml) | High/Critical findings = block merge |
| Signed commits on `main` | Branch protection (GitHub repo setting) | Verify required |
| Dependency updates | [`.github/dependabot.yml`](.github/dependabot.yml) (gomod + npm + github-actions, weekly) | Reactive |

Per the RAN-50 board ruling, the **OpenSSF Best Practices passing badge is the
only hard supply-chain gate**; Scorecard is best-effort and tracked but does
not block merge. Drops in the published Scorecard number trigger an
investigation issue rather than a build failure.

For the disclosure policy, fix SLAs, and the full hardening reference list
see [`SECURITY.md`](SECURITY.md).
22 changes: 22 additions & 0 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,3 +107,25 @@ publicly as [GitHub Issues](https://github.com/RandomCodeSpace/docsiq/issues).
Security reports are archived as
[GitHub Security Advisories](https://github.com/RandomCodeSpace/docsiq/security/advisories)
after coordinated disclosure.

## Hardening references

The supply-chain and code-quality controls backing this policy:

- [`.github/workflows/scorecard.yml`](.github/workflows/scorecard.yml) —
OpenSSF Scorecard analysis (push to `main` + weekly cron, SARIF
uploaded to the GitHub Security tab).
- [`.github/workflows/security.yml`](.github/workflows/security.yml) —
OSS-CLI security stack: Semgrep (SAST), osv-scanner (SCA), Trivy
(filesystem CVE), Gitleaks (secrets), jscpd (duplication),
`anchore/sbom-action` (SPDX + CycloneDX SBOMs).
- [`.github/workflows/codeql.yml`](.github/workflows/codeql.yml) —
GitHub CodeQL code scanning, SARIF in the Security tab.
- [`.github/dependabot.yml`](.github/dependabot.yml) — automated
dependency, GitHub Actions, and npm bumps (weekly).
- [`.bestpractices.json`](.bestpractices.json) — OpenSSF Best Practices
self-assessment (project
[12628](https://www.bestpractices.dev/en/projects/12628), badge
status: passing).
- GitHub repo settings — secret scanning, push protection, and private
vulnerability reporting are enabled at the repo level.
Loading