diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000000..7df4acc8e6 --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,67 @@ +name: CodeQL + +# Static analysis (SAST) for TypeScript/JavaScript and Python sources. +# Findings upload to the GitHub Security tab as SARIF. +# +# Advisory only on first introduction — see docs/plans/2026-05-03-001-feat-automated-security-scans-plan.md. +# Promote to a required check after baseline triage (operator decision). + +on: + pull_request: + branches: [main] + paths-ignore: ['**.md', 'docs/**', 'LICENSE'] + push: + branches: [main] + schedule: + # Weekly Monday 06:00 UTC — catches advisories newly published against + # already-merged code without waiting for the next PR. + - cron: '0 6 * * 1' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + +jobs: + analyze: + name: Analyze (${{ matrix.language }}) + runs-on: ubuntu-latest + timeout-minutes: 60 + permissions: + actions: read + contents: read + # security-events:write is what enables SARIF upload to the Security tab. + security-events: write + + strategy: + fail-fast: false + matrix: + language: [javascript-typescript, python] + + steps: + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + # Don't leave GITHUB_TOKEN in .git/config for downstream steps to read. + persist-credentials: false + + - name: Initialize CodeQL + uses: github/codeql-action/init@0daab03d71ff584ef619d027a3fd9146679c5d84 # v3.35.3 + with: + languages: ${{ matrix.language }} + queries: security-and-quality + # Exclude generated/vendored code; tune after first-run signal. + # gitnexus/vendor/ holds tree-sitter-proto sources (regenerated, not authored). + # CodeQL path filters use .gitignore-style globs and do NOT support + # brace expansion — list each generated parser file separately. + config: | + paths-ignore: + - '**/dist/**' + - '**/node_modules/**' + - 'gitnexus/vendor/**' + - 'gitnexus/src/core/parsing/**/parser.c' + - 'gitnexus/src/core/parsing/**/parser.js' + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@0daab03d71ff584ef619d027a3fd9146679c5d84 # v3.35.3 + with: + category: '/language:${{ matrix.language }}' diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml new file mode 100644 index 0000000000..ad06267c21 --- /dev/null +++ b/.github/workflows/dependency-review.yml @@ -0,0 +1,36 @@ +name: Dependency Review + +# Blocks PRs that introduce dependencies with high/critical known vulnerabilities. +# Reads the dependency graph diff between PR head and base. +# +# This is a required-check candidate after one week of clean runs +# (operator decision — see docs/plans/2026-05-03-001-feat-automated-security-scans-plan.md). + +on: + pull_request: + branches: [main] + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + review: + runs-on: ubuntu-latest + timeout-minutes: 10 + permissions: + contents: read + # pull-requests:write enables the inline summary comment on failure. + pull-requests: write + + steps: + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + + - name: Dependency Review + uses: actions/dependency-review-action@2031cfc080254a8a887f58cffee85186f0e49e48 # v4.9.0 + with: + fail-on-severity: high + comment-summary-in-pr: on-failure diff --git a/.github/workflows/gitleaks.yml b/.github/workflows/gitleaks.yml new file mode 100644 index 0000000000..cfe4d08562 --- /dev/null +++ b/.github/workflows/gitleaks.yml @@ -0,0 +1,45 @@ +name: Gitleaks + +# Deterministic in-CI secret scanning. Defense-in-depth on top of GitHub's +# native secret-scanning push protection (which is a repo Settings toggle — +# see SECURITY.md for the recommended admin action). +# +# PR runs scan the diff (fast); main pushes scan full history. + +on: + pull_request: + branches: [main] + push: + branches: [main] + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + +jobs: + gitleaks: + runs-on: ubuntu-latest + timeout-minutes: 10 + permissions: + contents: read + pull-requests: write + + steps: + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + # Full history needed for the on-push full-history scan; on PRs the + # action diffs against the base ref so the cost is bounded by the PR. + fetch-depth: 0 + # Don't bake the token into the cloned .git/config; downstream + # steps (and Gitleaks itself) don't need it for repo operations. + persist-credentials: false + + # No GITLEAKS_LICENSE secret is required for OSS / public-repo usage. + # If this repo becomes private, the action will require a license key. + - name: Gitleaks + uses: gitleaks/gitleaks-action@ff98106e4c7b2bc287b24eaf42907196329070c7 # v2.3.9 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITLEAKS_ENABLE_UPLOAD_ARTIFACT: true + GITLEAKS_ENABLE_SUMMARY: true diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 1fc95a13aa..d372c163a1 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -37,8 +37,12 @@ jobs: with: node-version: 20 registry-url: https://registry.npmjs.org - cache: npm - cache-dependency-path: gitnexus/package-lock.json + # Hermetic install for the published artifact — no cache carry-over + # from non-tag contexts. setup-node v5+ caches by default when a + # packageManager field is present in package.json, so the explicit + # opt-out is required to clear the zizmor cache-poisoning audit. + # ~30s slower per release; runs rarely. + package-manager-cache: false - name: Build gitnexus-shared run: npm install && npm run build working-directory: gitnexus-shared diff --git a/.github/workflows/release-candidate.yml b/.github/workflows/release-candidate.yml index 7413a02e14..5b598ee14a 100644 --- a/.github/workflows/release-candidate.yml +++ b/.github/workflows/release-candidate.yml @@ -137,8 +137,11 @@ jobs: with: node-version: 20 registry-url: https://registry.npmjs.org - cache: npm - cache-dependency-path: gitnexus/package-lock.json + # Hermetic install — release-candidate produces shipped artifacts. + # setup-node v5+ caches by default when a packageManager field is + # present in package.json; explicit opt-out is required to clear + # the zizmor cache-poisoning audit. See cache-poisoning audit. + package-manager-cache: false - name: Build gitnexus-shared run: npm install && npm run build diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml new file mode 100644 index 0000000000..b132b7c3f3 --- /dev/null +++ b/.github/workflows/scorecard.yml @@ -0,0 +1,58 @@ +name: Scorecard + +# OpenSSF Scorecard supply-chain posture check. Runs weekly + on main push + +# branch_protection_rule changes. SARIF uploads to the Security tab; the public +# badge URL resolves once the first scheduled run lands (see README badge wiring). + +on: + branch_protection_rule: + schedule: + - cron: '0 7 * * 1' + push: + branches: [main] + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: false + +permissions: read-all + +jobs: + analysis: + name: Scorecard analysis + runs-on: ubuntu-latest + timeout-minutes: 30 + permissions: + # Needed to upload SARIF results to the Security tab. + security-events: write + # Needed for the publish_results badge flow (OIDC). + id-token: write + contents: read + actions: read + + steps: + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + + - name: Run Scorecard + uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3 + with: + results_file: results.sarif + results_format: sarif + # publish_results enables the public Scorecard badge. + publish_results: true + + - name: Upload artifact + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: SARIF file + path: results.sarif + retention-days: 5 + + - name: Upload to Security tab + uses: github/codeql-action/upload-sarif@0daab03d71ff584ef619d027a3fd9146679c5d84 # v3.35.3 + with: + sarif_file: results.sarif diff --git a/.github/workflows/trivy.yml b/.github/workflows/trivy.yml new file mode 100644 index 0000000000..5bc5d17a4d --- /dev/null +++ b/.github/workflows/trivy.yml @@ -0,0 +1,73 @@ +name: Trivy Image Scan + +# Builds Dockerfile.cli and Dockerfile.web, then scans the resulting images +# for OS-package and language-package CVEs at HIGH/CRITICAL severity. +# Findings upload to the Security tab; record-only (does not block merges). +# +# NOT triggered on PRs — image builds are slow and base-image CVE churn +# shouldn't gate feature delivery. + +on: + push: + branches: [main] + schedule: + - cron: '0 8 * * 1' + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: false + +jobs: + scan: + name: Trivy (${{ matrix.image.name }}) + runs-on: ubuntu-latest + timeout-minutes: 30 + permissions: + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + image: + - { dockerfile: Dockerfile.cli, name: gitnexus-cli } + - { dockerfile: Dockerfile.web, name: gitnexus-web } + + steps: + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + + - name: Setup Buildx + uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0 + + - name: Build image (load locally for scan) + uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6.19.2 + with: + context: . + file: ${{ matrix.image.dockerfile }} + load: true + push: false + tags: scan-target:${{ matrix.image.name }} + + # aquasecurity/trivy-action versions < 0.35.0 are flagged by + # GHSA-69fq-xp46-6x23 (briefly compromised supply chain). Pinned to + # v0.36.0 (post-incident clean release) by commit SHA. + - name: Run Trivy + uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0 + with: + image-ref: scan-target:${{ matrix.image.name }} + format: sarif + output: trivy-${{ matrix.image.name }}.sarif + severity: HIGH,CRITICAL + # Hides CVEs with no available fix in the base image. + ignore-unfixed: true + exit-code: '0' + + - name: Upload to Security tab + uses: github/codeql-action/upload-sarif@0daab03d71ff584ef619d027a3fd9146679c5d84 # v3.35.3 + with: + sarif_file: trivy-${{ matrix.image.name }}.sarif + category: trivy-${{ matrix.image.name }} diff --git a/.github/workflows/workflow-lint.yml b/.github/workflows/workflow-lint.yml new file mode 100644 index 0000000000..3dd4924069 --- /dev/null +++ b/.github/workflows/workflow-lint.yml @@ -0,0 +1,58 @@ +name: Workflow Lint (zizmor) + +# Lints .github/workflows/** for known GitHub Actions security misconfigurations: +# unpinned Actions, dangerous ${{ ... }} interpolation in run: blocks, +# missing per-job permissions:, etc. +# +# Scoped to PRs that touch .github/** only — keeps off the typical PR critical path. + +on: + pull_request: + branches: [main] + paths: + - '.github/**' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + zizmor: + runs-on: ubuntu-latest + timeout-minutes: 10 + permissions: + contents: read + security-events: write + + steps: + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + + - name: Setup Python + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6 + with: + python-version: '3.12' + + - name: Install zizmor + # Pinned — resolves to whatever's latest on PyPI otherwise. + # Bump via Dependabot pip ecosystem (see .github/dependabot.yml). + run: pipx install zizmor==1.24.1 + + # Initial threshold: medium. High+ findings fail the job; medium findings + # appear in the Security tab without blocking. Tune after first run. + # Per-rule exemptions for pre-existing intentional patterns live in + # .github/zizmor.yml (each carries a documented mitigation). + - name: Run zizmor + run: zizmor --config .github/zizmor.yml --format sarif --min-severity medium . > zizmor.sarif + continue-on-error: true + + - name: Upload SARIF + uses: github/codeql-action/upload-sarif@0daab03d71ff584ef619d027a3fd9146679c5d84 # v3.35.3 + with: + sarif_file: zizmor.sarif + category: zizmor + + - name: Fail on high+ findings + run: zizmor --config .github/zizmor.yml --min-severity high . diff --git a/.github/zizmor.yml b/.github/zizmor.yml new file mode 100644 index 0000000000..c534679c1a --- /dev/null +++ b/.github/zizmor.yml @@ -0,0 +1,34 @@ +# zizmor config — pre-existing intentional patterns flagged on initial introduction. +# Each ignore below has a documented mitigation. Re-evaluate when the source workflow changes. +# +# To run zizmor locally with this config: +# zizmor --config .github/zizmor.yml . + +rules: + dangerous-triggers: + ignore: + # workflow_run is REQUIRED to post sticky comments on fork PRs — the + # default-branch privileged token isn't accessible from `pull_request` + # on a fork. Mitigated by: read-only `actions:read` + `contents:read` + # for artifact download; `pull-requests:write` is the only write scope; + # no checkout of fork code occurs. Header comment in the file documents. + - ci-report.yml + + # pull_request_target needed by claude-code-action to access secrets + # and post review comments on fork PRs. Mitigated by: PR checkouts pin + # the fork's HEAD SHA (not the branch ref) to prevent TOCTOU races, + # and claude-code-action sandboxes execution. Header comment documents. + - claude.yml + + # pull_request_target on the autolabel job needs `pull-requests:write` + # to apply labels. Mitigated by: release-drafter runs with `dry-run: + # true`, reads only `.github/release-drafter.yml` from the BASE ref, + # and the validate-title job (which runs untrusted `pull_request` + # context) holds no write permissions. Header comment documents. + - pr-labeler.yml + + # Note: cache-poisoning is NOT exempted. The two prior findings in + # publish.yml and release-candidate.yml were fixed structurally by + # dropping `cache: npm` from those workflows (matches the pattern used + # by PyO3/maturin for the same audit). See the commit that added this + # file for the rationale. diff --git a/README.md b/README.md index ade7a4879a..2dad7f2efa 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,9 @@ License: PolyForm Noncommercial + + OpenSSF Scorecard +

Enterprise (SaaS & Self-hosted) - akonlabs.com

diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000000..79ef97f6b2 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,67 @@ +# Security Policy + +## Supported Versions + +GitNexus is developed on `main`. Security fixes are applied to the latest released minor on npm (`gitnexus`) and to the published Docker images (`Dockerfile.cli`, `Dockerfile.web`). Older minors are not back-patched. + +## Reporting a Vulnerability + +**Please do not open a public GitHub issue for security reports.** + +Use **GitHub Private Vulnerability Reporting** for this repository: + +→ https://github.com/abhigyanpatwari/GitNexus/security/advisories/new + +Please include: + +- A description of the issue and its potential impact +- Steps to reproduce (a minimal repro repo or commit hash if possible) +- The affected version(s) — `npm view gitnexus version`, image digest, or commit SHA +- Any suggested mitigation + +### What to expect + +- **Acknowledgement:** best-effort within 5 business days, subject to maintainer capacity. +- **Triage:** we will confirm whether the report is in scope, request clarifications if needed, and propose a fix timeline. +- **Disclosure:** coordinated. We will agree on a disclosure date with you before publishing an advisory. + +### Scope + +In scope: + +- The `gitnexus` CLI and MCP server (`gitnexus/`) +- The `gitnexus-web` thin client (`gitnexus-web/`) +- The `gitnexus-shared` types package (`gitnexus-shared/`) +- The published Docker images (`Dockerfile.cli`, `Dockerfile.web`) +- GitHub Actions workflows in `.github/workflows/` + +Out of scope: + +- Vulnerabilities in third-party dependencies that we have no influence over (please report upstream; if a viable mitigation exists at the GitNexus layer, that's in scope). +- Issues requiring physical access to a developer machine or a compromised local environment. +- Theoretical attacks without a practical exploit against a default GitNexus deployment. + +## Recommended Hardening for Forks and Self-Hosted Deployments + +If you fork GitNexus or self-host it, we recommend enabling the following in your repository's **Settings → Code security and analysis**: + +- **Private vulnerability reporting** — the channel described above. +- **Dependabot alerts** — alerts on advisories affecting your dependencies. +- **Dependabot security updates** — automated PRs for security patches (this repo's `.github/dependabot.yml` already covers version updates). +- **Secret scanning** and **Push protection** — blocks pushes that introduce known secret patterns. Defense-in-depth on top of the in-CI Gitleaks scan documented below. +- **Code scanning** — surfaces SARIF results from CodeQL, Trivy, Scorecard, and zizmor in one place. + +## Automated Scans Running in CI + +This repository runs the following scans automatically. Findings appear under the repository's **Security → Code scanning** tab. + +| Scan | Tool | Trigger | Action on finding | +|------|------|---------|-------------------| +| Static analysis (JS/TS, Python) | [CodeQL](https://github.com/github/codeql-action) | PR, `main` push, weekly | Advisory (Security tab) | +| Dependency vulnerabilities (PR diff) | [`dependency-review-action`](https://github.com/actions/dependency-review-action) | PR | **Blocks PR** at `high+` severity | +| Secret scanning | [Gitleaks](https://github.com/gitleaks/gitleaks-action) | PR, `main` push | **Blocks PR** on default rules | +| Supply-chain posture | [OpenSSF Scorecard](https://github.com/ossf/scorecard-action) | Weekly, `main` push | Advisory (Security tab + public badge) | +| Workflow lint | [zizmor](https://github.com/woodruffw/zizmor) | PR (touching `.github/**`) | **Blocks PR** at `high+` severity | +| Container image scan | [Trivy](https://github.com/aquasecurity/trivy-action) | Weekly, `main` push | Advisory (Security tab) | + +Dependency version updates are managed separately by Dependabot — see `.github/dependabot.yml`.