From 3cc30937d7aca238e247d7781441c3aab94a5d8c Mon Sep 17 00:00:00 2001 From: "Hel.Isa" Date: Sat, 2 May 2026 16:31:41 -0400 Subject: [PATCH 1/5] Implement reusable security gate workflows and update documentation for v2 --- .github/workflows/reusable-dashboard.yml | 22 +++- .github/workflows/reusable-security-gate.yml | 119 +++++++++++++++++++ .github/workflows/security-baseline.yml | 41 ++----- README.md | 67 +++++++++-- SECURITY.md | 6 +- docs/architecture.md | 32 ++++- docs/onboarding.md | 98 +++++++++++++++ docs/presets.md | 63 ++++++++++ docs/secure-coding-standard.md | 2 +- examples/security-gate-audit.yml | 24 ++++ examples/security-gate-strict.yml | 24 ++++ 11 files changed, 438 insertions(+), 60 deletions(-) create mode 100644 .github/workflows/reusable-security-gate.yml create mode 100644 docs/onboarding.md create mode 100644 docs/presets.md create mode 100644 examples/security-gate-audit.yml create mode 100644 examples/security-gate-strict.yml diff --git a/.github/workflows/reusable-dashboard.yml b/.github/workflows/reusable-dashboard.yml index 5424bad..14582f3 100644 --- a/.github/workflows/reusable-dashboard.yml +++ b/.github/workflows/reusable-dashboard.yml @@ -13,6 +13,16 @@ on: required: false default: false type: boolean + gate_repository: + description: Repository containing Security Gate scripts and dashboard assets + required: false + default: hel-isa/security-gate + type: string + gate_ref: + description: Git ref for Security Gate scripts and dashboard assets + required: false + default: v2 + type: string permissions: contents: read @@ -27,8 +37,12 @@ jobs: url: ${{ steps.deployment.outputs.page_url }} steps: - - name: Checkout repository + - name: Checkout Security Gate assets uses: actions/checkout@v4 + with: + repository: ${{ inputs.gate_repository }} + ref: ${{ inputs.gate_ref }} + path: security-gate - name: Download security artifacts uses: actions/download-artifact@v4 @@ -41,7 +55,7 @@ jobs: run: | set -euo pipefail mkdir -p reports/normalized - python scripts/aggregate_results.py \ + python security-gate/scripts/aggregate_results.py \ --input-dir reports/raw \ --output-file reports/normalized/dashboard-data.json \ --repo-name "${{ inputs.repo_name || github.repository }}" @@ -49,9 +63,9 @@ jobs: - name: Build static dashboard package run: | set -euo pipefail - python scripts/generate_dashboard.py \ + python security-gate/scripts/generate_dashboard.py \ --data-file reports/normalized/dashboard-data.json \ - --dashboard-dir dashboard \ + --dashboard-dir security-gate/dashboard \ --output-dir reports/dashboard - name: Verify dashboard output diff --git a/.github/workflows/reusable-security-gate.yml b/.github/workflows/reusable-security-gate.yml new file mode 100644 index 0000000..d361eee --- /dev/null +++ b/.github/workflows/reusable-security-gate.yml @@ -0,0 +1,119 @@ +name: Reusable - Security Gate + +on: + workflow_call: + inputs: + mode: + description: Gate mode. Use audit for non-blocking scans or strict to fail on findings. + required: false + default: audit + type: string + semgrep_config: + description: Semgrep config or ruleset. + required: false + default: auto + type: string + gitleaks_config_path: + description: Path to a Gitleaks config file in the scanned repository. + required: false + default: .gitleaks.toml + type: string + sbom_scan_path: + description: Path to scan for SBOM generation. + required: false + default: . + type: string + repo_name: + description: Repository name override for dashboard output. + required: false + default: '' + type: string + deploy_pages: + description: Deploy the dashboard to GitHub Pages on eligible runs. + required: false + default: false + type: boolean + gate_repository: + description: Repository containing Security Gate scripts and dashboard assets. + required: false + default: hel-isa/security-gate + type: string + gate_ref: + description: Git ref for Security Gate scripts and dashboard assets. + required: false + default: v2 + type: string + +permissions: + contents: read + pages: write + id-token: write + +jobs: + validate: + name: Validate Gate Configuration + runs-on: ubuntu-latest + outputs: + fail_on_findings: ${{ steps.mode.outputs.fail_on_findings }} + steps: + - name: Resolve mode + id: mode + run: | + set -euo pipefail + case "${{ inputs.mode }}" in + audit|non-strict) + echo "fail_on_findings=false" >> "$GITHUB_OUTPUT" + ;; + strict) + echo "fail_on_findings=true" >> "$GITHUB_OUTPUT" + ;; + *) + echo "Unsupported security gate mode: ${{ inputs.mode }}" + echo "Use audit, non-strict, or strict." + exit 1 + ;; + esac + + secrets: + name: Secrets Detection + needs: validate + uses: ./.github/workflows/reusable-secrets.yml + with: + fail_on_findings: ${{ needs.validate.outputs.fail_on_findings == 'true' }} + config_path: ${{ inputs.gitleaks_config_path }} + + sast: + name: SAST Scan + needs: validate + uses: ./.github/workflows/reusable-sast.yml + with: + fail_on_findings: ${{ needs.validate.outputs.fail_on_findings == 'true' }} + config: ${{ inputs.semgrep_config }} + + sca: + name: SCA Scan + needs: validate + uses: ./.github/workflows/reusable-sca.yml + with: + fail_on_findings: ${{ needs.validate.outputs.fail_on_findings == 'true' }} + + sbom: + name: SBOM Generation + uses: ./.github/workflows/reusable-sbom.yml + with: + scan_path: ${{ inputs.sbom_scan_path }} + + dashboard: + name: Security Dashboard + needs: + - secrets + - sast + - sca + - sbom + if: always() + uses: ./.github/workflows/reusable-dashboard.yml + with: + repo_name: ${{ inputs.repo_name }} + deploy_pages: ${{ inputs.deploy_pages }} + gate_repository: ${{ inputs.gate_repository }} + gate_ref: ${{ inputs.gate_ref }} diff --git a/.github/workflows/security-baseline.yml b/.github/workflows/security-baseline.yml index f9a60cd..2905351 100644 --- a/.github/workflows/security-baseline.yml +++ b/.github/workflows/security-baseline.yml @@ -12,38 +12,11 @@ permissions: id-token: write jobs: - secrets: - name: Secrets Detection - uses: hel-isa/security-gate/.github/workflows/reusable-secrets.yml@main + security-gate: + name: Security Gate + uses: ./.github/workflows/reusable-security-gate.yml with: - fail_on_findings: true - - sast: - name: SAST Scan - uses: hel-isa/security-gate/.github/workflows/reusable-sast.yml@main - with: - fail_on_findings: true - config: auto - - sca: - name: SCA Scan - uses: hel-isa/security-gate/.github/workflows/reusable-sca.yml@main - with: - fail_on_findings: true - - sbom: - name: SBOM Generation - uses: hel-isa/security-gate/.github/workflows/reusable-sbom.yml@main - - dashboard: - name: Security Dashboard - needs: - - secrets - - sast - - sca - - sbom - if: always() - uses: hel-isa/security-gate/.github/workflows/reusable-dashboard.yml@main - with: - repo_name: ${{ github.repository }} - deploy_pages: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }} + mode: strict + semgrep_config: auto + repo_name: ${{ github.repository }} + deploy_pages: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }} diff --git a/README.md b/README.md index be17205..2906ced 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,19 @@ -# Security Guardrails as a Service (v1) +# Security Gate (v2) -A GitHub-native, reusable, modular DevSecOps baseline that uses only free/open-source tools and does **not** depend on GitHub Advanced Security. +A GitHub-native, reusable security gate that lets approved repositories scan code with a practical default workflow. It uses free/open-source tools and does **not** depend on GitHub Advanced Security. ## Project Purpose -This project provides a production-minded starting point for security checks in CI: -- modular reusable workflows -- standardized JSON artifacts -- lightweight static dashboard reporting -- Python-based data normalization for extensibility +Security Gate provides a productized starting point for security checks in CI: + +- one default workflow users can call remotely +- audit and strict presets for non-blocking or blocking scans +- language-agnostic baseline controls +- optional low-level reusable workflows for advanced teams +- standardized JSON artifacts and lightweight dashboard reporting ## Architecture at a Glance -- Orchestrator workflow: `.github/workflows/security-baseline.yml` +- Product entry point: `.github/workflows/reusable-security-gate.yml` +- Local baseline workflow: `.github/workflows/security-baseline.yml` - Independent reusable controls: - `reusable-secrets.yml` (Gitleaks) - `reusable-sast.yml` (Semgrep) @@ -26,8 +29,37 @@ Detailed design is documented in `docs/architecture.md`. - SCA: OSV-Scanner - SBOM: Syft (CycloneDX JSON) +## Quick Start for Approved Repositories + +Copy one of the templates from `examples/` into the target repository as `.github/workflows/security-gate.yml`. + +Start in audit mode: + +```yaml +jobs: + security-gate: + name: Security Gate + uses: hel-isa/security-gate/.github/workflows/reusable-security-gate.yml@v2 + with: + mode: audit + semgrep_config: auto + repo_name: ${{ github.repository }} + gate_ref: v2 +``` + +Switch to strict mode when the team is ready to block on findings: + +```yaml +with: + mode: strict + semgrep_config: auto +``` + +See `docs/onboarding.md` for the full copy-paste workflow and `docs/presets.md` for preset guidance. + ## Repository Layout - `.github/workflows/` - orchestrator and reusable workflow modules +- `examples/` - copy-paste workflows for consuming repositories - `scripts/` - Python helpers for normalization/dashboard packaging - `dashboard/` - static dashboard assets (HTML/CSS/JS) - `docs/` - architecture and secure coding baseline docs @@ -35,11 +67,14 @@ Detailed design is documented in `docs/architecture.md`. - `SECURITY.md` - vulnerability reporting + scope - `pull_request_template.md` - secure review checklist -## How to Enable Workflows -1. Push this repository to GitHub. -2. Ensure GitHub Actions are enabled for the repository. -3. Confirm default branch is `main` (or adjust trigger branch in `security-baseline.yml`). -4. Open a PR or push to `main` to trigger scans. +## Presets + +| Preset | Mode | Behavior | +| --- | --- | --- | +| Audit | `audit` | Runs scans and reports findings without blocking merges. | +| Strict | `strict` | Runs scans and fails when blocking findings are detected. | + +`non-strict` is accepted as an alias for `audit`. ## Artifacts and Dashboard Each control uploads artifacts in JSON format: @@ -56,6 +91,10 @@ The dashboard workflow downloads artifacts, normalizes results into `dashboard-d Open `index.html` locally from the dashboard artifact folder to view the report. +## Advanced Usage + +Advanced teams can call individual reusable workflows directly when they need custom composition. Most repositories should use `reusable-security-gate.yml`. + ## Local Usage (where applicable) - Aggregation script: ```bash @@ -67,8 +106,10 @@ Open `index.html` locally from the dashboard artifact folder to view the report. ``` ## Roadmap Ideas (v2+) +- Stable release tags for approved consumers - Severity mapping improvements for OSV and custom policies - Trend history across runs - Multi-repo rollup dashboards - SARIF export option - Optional Slack/Teams notifications +- v3 bot to inspect repositories and generate tuned workflow/configuration diff --git a/SECURITY.md b/SECURITY.md index 4ae49c3..d85e796 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -9,17 +9,19 @@ Include: - Potential impact - Suggested remediation (if known) -## Supported Scope (v1) -This repository provides CI guardrails for: +## Supported Scope (v2) +This repository provides a reusable CI security gate for approved repositories: - Secrets detection (Gitleaks) - SAST (Semgrep) - SCA (OSV-Scanner) - SBOM generation (Syft) +- Static dashboard artifacts ## Out of Scope - Runtime protection - Cloud workload posture management - Proprietary GHAS-only features +- Approval management for consuming repositories ## Disclosure Expectations - Do not publicly disclose vulnerabilities before triage. diff --git a/docs/architecture.md b/docs/architecture.md index 12ee3cb..1c66f6e 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -1,17 +1,28 @@ -# Architecture - Security Guardrails as a Service (v1) +# Architecture - Security Gate (v2) ## Overview -Version 1 is built around **modular reusable GitHub Actions workflows**. Each security control executes independently, produces JSON output, and publishes artifacts. A dedicated dashboard workflow pulls those artifacts and produces normalized reporting. +Version 2 is built around a **product-level reusable GitHub Actions workflow** backed by modular controls. Approved repositories call one gate workflow, choose a preset, and receive standardized scan artifacts plus a dashboard. ## Workflow Design +- `reusable-security-gate.yml`: product entry point for approved repositories. +- `security-baseline.yml`: local workflow used by this repository to run the gate against itself. - `reusable-secrets.yml`: Gitleaks scan for secret exposure. - `reusable-sast.yml`: Semgrep scan for static code risks. - `reusable-sca.yml`: OSV-Scanner scan for vulnerable dependencies. - `reusable-sbom.yml`: Syft SBOM generation in CycloneDX JSON. - `reusable-dashboard.yml`: report aggregation and static dashboard creation. -- `security-baseline.yml`: orchestrator that calls each reusable workflow. -Each control is callable through `workflow_call`, enabling future repositories to consume any subset of controls. +Most repositories should call `reusable-security-gate.yml`. Each lower-level control is still callable through `workflow_call`, enabling advanced repositories to consume any subset of controls. + +## Product Presets + +The gate exposes a small preset surface: + +- `mode: audit` or `mode: non-strict` runs all controls and reports findings without blocking. +- `mode: strict` runs the same baseline and fails when blocking controls detect findings. +- `semgrep_config: auto` keeps SAST language-aware without requiring per-language setup. + +This keeps the default path language-agnostic while preserving escape hatches for custom policy. ## Artifact Flow 1. Each control writes machine-readable JSON into `reports/`. @@ -20,9 +31,18 @@ Each control is callable through `workflow_call`, enabling future repositories t - `semgrep-report` - `osv-report` - `sbom-report` -3. Dashboard workflow downloads `*-report` artifacts, normalizes findings with Python, and packages a static dashboard artifact. +3. Dashboard workflow downloads `*-report` artifacts. +4. Dashboard workflow checks out the Security Gate repository assets through `gate_repository` and `gate_ref`. +5. Dashboard workflow normalizes findings with Python and packages a static dashboard artifact. + +## Approved Repository Flow + +1. A repository owner copies an example workflow into `.github/workflows/security-gate.yml`. +2. The workflow calls `hel-isa/security-gate/.github/workflows/reusable-security-gate.yml@v2`. +3. The first run should use audit mode to establish a baseline. +4. The repository can switch to strict mode when findings are understood and enforcement is accepted. -## Why Separate Runners Are Not Required for v1 +## Why Separate Runners Are Not Required for v2 - GitHub-hosted runners are enough for baseline scans. - Runtime is acceptable for most small/medium repositories. - Artifact handoff provides sufficient inter-job communication. diff --git a/docs/onboarding.md b/docs/onboarding.md new file mode 100644 index 0000000..f2cd915 --- /dev/null +++ b/docs/onboarding.md @@ -0,0 +1,98 @@ +# Security Gate Onboarding + +Security Gate is designed for approved repositories that need a practical default security scan without rebuilding a DevSecOps workflow from scratch. + +## Choose a Preset + +Use one of these presets first. You can tune individual controls later. + +| Preset | Mode | Best for | Behavior | +| --- | --- | --- | --- | +| Audit | `audit` | First rollout, legacy repos, training | Runs all scans, publishes reports, does not block merges on findings. | +| Strict | `strict` | Protected branches, production services | Runs all scans and fails when secrets, SAST, or dependency findings exist. | + +`non-strict` is accepted as an alias for `audit`. + +## Quick Start + +1. Confirm the repository is approved to use this shared gate. +2. Copy one template into the target repository as `.github/workflows/security-gate.yml`. +3. Start with `examples/security-gate-audit.yml` for the first run. +4. Open a pull request or run the workflow manually. +5. Review the `security-dashboard` artifact. +6. Move to `mode: strict` when the team is ready to block on findings. + +## Recommended Default + +Use this for most repositories during onboarding: + +```yaml +name: Security Gate + +on: + pull_request: + push: + branches: + - main + workflow_dispatch: + +permissions: + contents: read + pages: write + id-token: write + +jobs: + security-gate: + name: Security Gate + uses: hel-isa/security-gate/.github/workflows/reusable-security-gate.yml@v2 + with: + mode: audit + semgrep_config: auto + repo_name: ${{ github.repository }} + gate_ref: v2 + deploy_pages: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }} +``` + +## Inputs + +| Input | Default | Description | +| --- | --- | --- | +| `mode` | `audit` | `audit`, `non-strict`, or `strict`. Strict mode fails jobs when findings are detected. | +| `semgrep_config` | `auto` | Semgrep ruleset. Keep `auto` for language-agnostic defaults. | +| `gitleaks_config_path` | `.gitleaks.toml` | Optional Gitleaks config path in the target repository. A default config is created when missing. | +| `sbom_scan_path` | `.` | Path used by Syft to generate the SBOM. | +| `repo_name` | caller repository | Repository name shown in dashboard data. | +| `deploy_pages` | `false` | Publishes the dashboard to GitHub Pages when enabled. | +| `gate_repository` | `hel-isa/security-gate` | Repository containing Security Gate scripts and dashboard assets. | +| `gate_ref` | `v2` | Git ref used to load Security Gate scripts and dashboard assets. | + +## Approved Use Model + +This project is intended for repositories and users approved by the owner of the shared Security Gate repository. + +Approved users can: + +- call the reusable workflow from their repository +- use audit mode to learn the current security posture +- switch to strict mode when the team accepts blocking behavior +- add local tool config files when they need repository-specific tuning + +Repository owners should: + +- pin the workflow reference to a stable branch or tag +- keep `gate_ref` aligned with the workflow version they call +- review findings before enabling strict mode +- document accepted exceptions in their own repository +- avoid storing sensitive scan outputs in public artifacts + +## Advanced Configuration + +Advanced users can call individual reusable workflows directly: + +- `.github/workflows/reusable-secrets.yml` +- `.github/workflows/reusable-sast.yml` +- `.github/workflows/reusable-sca.yml` +- `.github/workflows/reusable-sbom.yml` +- `.github/workflows/reusable-dashboard.yml` + +Use the product-level `reusable-security-gate.yml` unless you have a clear reason to compose the controls yourself. diff --git a/docs/presets.md b/docs/presets.md new file mode 100644 index 0000000..a5bac54 --- /dev/null +++ b/docs/presets.md @@ -0,0 +1,63 @@ +# Security Gate Presets + +Presets are lightweight workflow settings. They keep onboarding simple while still allowing teams to tune the gate over time. + +## Audit Preset + +Use audit mode first for most repositories. + +```yaml +with: + mode: audit + semgrep_config: auto +``` + +Audit mode runs: + +- Gitleaks secrets detection +- Semgrep SAST with language-aware defaults +- OSV-Scanner dependency vulnerability checks +- Syft SBOM generation +- dashboard aggregation + +Findings are reported but do not block the workflow. + +## Strict Preset + +Use strict mode when the repository is ready for enforcement. + +```yaml +with: + mode: strict + semgrep_config: auto +``` + +Strict mode runs the same language-agnostic baseline and fails the gate when findings are detected by blocking controls. + +## Custom Policy Preset + +Use this when a repository needs stronger Semgrep rules or local Gitleaks tuning. + +```yaml +with: + mode: strict + semgrep_config: p/ci + gitleaks_config_path: .security/gitleaks.toml +``` + +Recommended local files: + +- `.security/gitleaks.toml` for allowlists and repository-specific secret patterns +- `.semgrepignore` for generated code or vendored folders +- repository documentation for accepted risk decisions + +## Language-Agnostic Baseline + +The default gate avoids language-specific setup: + +- Gitleaks scans repository content for secrets. +- Semgrep `auto` selects rules based on detected languages. +- OSV-Scanner recursively detects supported manifests and lockfiles. +- Syft generates an SBOM from the repository path. + +Teams can add language-specific policies later without changing the default adoption path. diff --git a/docs/secure-coding-standard.md b/docs/secure-coding-standard.md index 3a9bad9..24fab5d 100644 --- a/docs/secure-coding-standard.md +++ b/docs/secure-coding-standard.md @@ -1,4 +1,4 @@ -# Secure Coding Standard (ASVS-inspired, v1) +# Secure Coding Standard (ASVS-inspired, v2) ## Purpose This baseline defines minimum secure coding expectations for pull requests and mainline merges. diff --git a/examples/security-gate-audit.yml b/examples/security-gate-audit.yml new file mode 100644 index 0000000..b87855d --- /dev/null +++ b/examples/security-gate-audit.yml @@ -0,0 +1,24 @@ +name: Security Gate + +on: + pull_request: + push: + branches: + - main + workflow_dispatch: + +permissions: + contents: read + pages: write + id-token: write + +jobs: + security-gate: + name: Security Gate + uses: hel-isa/security-gate/.github/workflows/reusable-security-gate.yml@v2 + with: + mode: audit + semgrep_config: auto + repo_name: ${{ github.repository }} + gate_ref: v2 + deploy_pages: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }} diff --git a/examples/security-gate-strict.yml b/examples/security-gate-strict.yml new file mode 100644 index 0000000..5231631 --- /dev/null +++ b/examples/security-gate-strict.yml @@ -0,0 +1,24 @@ +name: Security Gate + +on: + pull_request: + push: + branches: + - main + workflow_dispatch: + +permissions: + contents: read + pages: write + id-token: write + +jobs: + security-gate: + name: Security Gate + uses: hel-isa/security-gate/.github/workflows/reusable-security-gate.yml@v2 + with: + mode: strict + semgrep_config: auto + repo_name: ${{ github.repository }} + gate_ref: v2 + deploy_pages: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }} From 3c65f1aca20a618938a3219a285cbdb5c623e8a4 Mon Sep 17 00:00:00 2001 From: "Hel.Isa" Date: Sat, 2 May 2026 16:38:03 -0400 Subject: [PATCH 2/5] Update workflow references and documentation for v2.0.0 release --- CHANGELOG.md | 17 ++++++++ README.md | 11 +++-- VERSION | 1 + docs/architecture.md | 2 +- docs/onboarding.md | 6 +-- docs/release-management.md | 70 +++++++++++++++++++++++++++++++ examples/security-gate-audit.yml | 4 +- examples/security-gate-strict.yml | 4 +- 8 files changed, 103 insertions(+), 12 deletions(-) create mode 100644 CHANGELOG.md create mode 100644 VERSION create mode 100644 docs/release-management.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..bff337d --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,17 @@ +# Changelog + +## v2.0.0 - Initial Product Release + +Stable release for approved repositories that need a reusable Security Gate workflow. + +### Added + +- Product-level reusable workflow: `.github/workflows/reusable-security-gate.yml`. +- Audit and strict presets through the `mode` input. +- Copy-paste consumer workflow examples for audit and strict rollout. +- Onboarding, preset, and architecture documentation for approved consumers. +- Dashboard asset checkout through `gate_repository` and `gate_ref` so consumers do not need local dashboard scripts. + +### Release Notes + +Approved consumers should pin both the reusable workflow reference and `gate_ref` to `v2.0.0`. diff --git a/README.md b/README.md index 2906ced..45c2a31 100644 --- a/README.md +++ b/README.md @@ -39,12 +39,12 @@ Start in audit mode: jobs: security-gate: name: Security Gate - uses: hel-isa/security-gate/.github/workflows/reusable-security-gate.yml@v2 + uses: hel-isa/security-gate/.github/workflows/reusable-security-gate.yml@v2.0.0 with: mode: audit semgrep_config: auto repo_name: ${{ github.repository }} - gate_ref: v2 + gate_ref: v2.0.0 ``` Switch to strict mode when the team is ready to block on findings: @@ -55,7 +55,7 @@ with: semgrep_config: auto ``` -See `docs/onboarding.md` for the full copy-paste workflow and `docs/presets.md` for preset guidance. +See `docs/onboarding.md` for the full copy-paste workflow, `docs/presets.md` for preset guidance, and `docs/release-management.md` for stable tag usage. ## Repository Layout - `.github/workflows/` - orchestrator and reusable workflow modules @@ -95,6 +95,10 @@ Open `index.html` locally from the dashboard artifact folder to view the report. Advanced teams can call individual reusable workflows directly when they need custom composition. Most repositories should use `reusable-security-gate.yml`. +## Stable Releases + +Approved consumers should pin to immutable release tags such as `v2.0.0`. Keep the workflow reference and `gate_ref` aligned so the reusable workflow and dashboard assets come from the same release. + ## Local Usage (where applicable) - Aggregation script: ```bash @@ -106,7 +110,6 @@ Advanced teams can call individual reusable workflows directly when they need cu ``` ## Roadmap Ideas (v2+) -- Stable release tags for approved consumers - Severity mapping improvements for OSV and custom policies - Trend history across runs - Multi-repo rollup dashboards diff --git a/VERSION b/VERSION new file mode 100644 index 0000000..227cea2 --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +2.0.0 diff --git a/docs/architecture.md b/docs/architecture.md index 1c66f6e..f25cf69 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -38,7 +38,7 @@ This keeps the default path language-agnostic while preserving escape hatches fo ## Approved Repository Flow 1. A repository owner copies an example workflow into `.github/workflows/security-gate.yml`. -2. The workflow calls `hel-isa/security-gate/.github/workflows/reusable-security-gate.yml@v2`. +2. The workflow calls `hel-isa/security-gate/.github/workflows/reusable-security-gate.yml@v2.0.0`. 3. The first run should use audit mode to establish a baseline. 4. The repository can switch to strict mode when findings are understood and enforcement is accepted. diff --git a/docs/onboarding.md b/docs/onboarding.md index f2cd915..5d4c482 100644 --- a/docs/onboarding.md +++ b/docs/onboarding.md @@ -44,12 +44,12 @@ permissions: jobs: security-gate: name: Security Gate - uses: hel-isa/security-gate/.github/workflows/reusable-security-gate.yml@v2 + uses: hel-isa/security-gate/.github/workflows/reusable-security-gate.yml@v2.0.0 with: mode: audit semgrep_config: auto repo_name: ${{ github.repository }} - gate_ref: v2 + gate_ref: v2.0.0 deploy_pages: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }} ``` @@ -64,7 +64,7 @@ jobs: | `repo_name` | caller repository | Repository name shown in dashboard data. | | `deploy_pages` | `false` | Publishes the dashboard to GitHub Pages when enabled. | | `gate_repository` | `hel-isa/security-gate` | Repository containing Security Gate scripts and dashboard assets. | -| `gate_ref` | `v2` | Git ref used to load Security Gate scripts and dashboard assets. | +| `gate_ref` | `v2` | Git ref used to load Security Gate scripts and dashboard assets. Approved consumers should set this to the same release tag as the workflow reference. | ## Approved Use Model diff --git a/docs/release-management.md b/docs/release-management.md new file mode 100644 index 0000000..211e24a --- /dev/null +++ b/docs/release-management.md @@ -0,0 +1,70 @@ +# Release Management + +Security Gate consumers should pin to stable release tags. This keeps approved repositories protected from unexpected workflow changes. + +## Tag Strategy + +Use semantic version tags for immutable releases: + +- `v2.0.0`: exact release, recommended for approved consumers +- `v2.0.1`: patch release for compatible fixes +- `v2.1.0`: minor release for compatible features or new optional inputs +- `v3.0.0`: breaking release + +The `v2` branch may continue to track active v2 development. Do not ask approved consumers to depend on `@v2` unless they explicitly accept moving updates. + +## Consumer Pinning + +Approved repositories should pin the reusable workflow and dashboard assets to the same release: + +```yaml +jobs: + security-gate: + name: Security Gate + uses: hel-isa/security-gate/.github/workflows/reusable-security-gate.yml@v2.0.0 + with: + mode: audit + semgrep_config: auto + repo_name: ${{ github.repository }} + gate_ref: v2.0.0 +``` + +Keep these aligned: + +- workflow reference: `@v2.0.0` +- dashboard/script reference: `gate_ref: v2.0.0` + +## Release Checklist + +1. Update `VERSION`. +2. Update `CHANGELOG.md`. +3. Run local validation: + + ```bash + ruby -e 'require "yaml"; Dir[".github/workflows/*.yml", "examples/*.yml"].each { |f| YAML.load_file(f) }; puts "workflow yaml parse ok"' + PYTHONPYCACHEPREFIX=/private/tmp/security-gate-pycache python3 -m py_compile scripts/aggregate_results.py scripts/generate_dashboard.py + ``` + +4. Commit the release changes. +5. Create an annotated tag: + + ```bash + git tag -a v2.0.0 -m "Release v2.0.0" + ``` + +6. Push the branch and tag: + + ```bash + git push origin v2 + git push origin v2.0.0 + ``` + +7. Ask one approved repository to run the audit template before announcing the release more broadly. + +## Compatibility Promise + +Patch releases should not break existing approved consumers. + +Minor releases may add optional inputs or new artifacts, but should preserve existing input names, artifact names, and default behavior. + +Major releases may change workflow contracts, artifact schemas, or enforcement behavior. diff --git a/examples/security-gate-audit.yml b/examples/security-gate-audit.yml index b87855d..b91e1eb 100644 --- a/examples/security-gate-audit.yml +++ b/examples/security-gate-audit.yml @@ -15,10 +15,10 @@ permissions: jobs: security-gate: name: Security Gate - uses: hel-isa/security-gate/.github/workflows/reusable-security-gate.yml@v2 + uses: hel-isa/security-gate/.github/workflows/reusable-security-gate.yml@v2.0.0 with: mode: audit semgrep_config: auto repo_name: ${{ github.repository }} - gate_ref: v2 + gate_ref: v2.0.0 deploy_pages: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }} diff --git a/examples/security-gate-strict.yml b/examples/security-gate-strict.yml index 5231631..10e8124 100644 --- a/examples/security-gate-strict.yml +++ b/examples/security-gate-strict.yml @@ -15,10 +15,10 @@ permissions: jobs: security-gate: name: Security Gate - uses: hel-isa/security-gate/.github/workflows/reusable-security-gate.yml@v2 + uses: hel-isa/security-gate/.github/workflows/reusable-security-gate.yml@v2.0.0 with: mode: strict semgrep_config: auto repo_name: ${{ github.repository }} - gate_ref: v2 + gate_ref: v2.0.0 deploy_pages: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }} From d5ad78499e8c28bb72ec251e29d5a0df2a34118b Mon Sep 17 00:00:00 2001 From: "Hel.Isa" Date: Sat, 2 May 2026 16:46:19 -0400 Subject: [PATCH 3/5] Update security gate mode to audit and enhance documentation on preset usage --- .github/workflows/security-baseline.yml | 2 +- docs/presets.md | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/security-baseline.yml b/.github/workflows/security-baseline.yml index 2905351..6abfba4 100644 --- a/.github/workflows/security-baseline.yml +++ b/.github/workflows/security-baseline.yml @@ -16,7 +16,7 @@ jobs: name: Security Gate uses: ./.github/workflows/reusable-security-gate.yml with: - mode: strict + mode: audit semgrep_config: auto repo_name: ${{ github.repository }} deploy_pages: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }} diff --git a/docs/presets.md b/docs/presets.md index a5bac54..911e5e1 100644 --- a/docs/presets.md +++ b/docs/presets.md @@ -34,6 +34,8 @@ with: Strict mode runs the same language-agnostic baseline and fails the gate when findings are detected by blocking controls. +Before switching to strict mode, run audit mode first and review the dashboard artifact. Strict mode is intentionally blocking: if Semgrep reports findings, the SAST job exits with failure until the findings are fixed, suppressed with repository-owned configuration, or accepted through a documented policy. + ## Custom Policy Preset Use this when a repository needs stronger Semgrep rules or local Gitleaks tuning. From c6fe8d20e37e7b483f842a552f8dc764dc2c9810 Mon Sep 17 00:00:00 2001 From: "Hel.Isa" Date: Sat, 2 May 2026 17:00:42 -0400 Subject: [PATCH 4/5] Refactor deployment configuration to disable GitHub Pages by default across workflows --- .github/workflows/reusable-dashboard.yml | 20 +++++++++++--------- .github/workflows/security-baseline.yml | 2 +- docs/onboarding.md | 8 +++++++- examples/security-gate-audit.yml | 2 +- examples/security-gate-strict.yml | 2 +- 5 files changed, 21 insertions(+), 13 deletions(-) diff --git a/.github/workflows/reusable-dashboard.yml b/.github/workflows/reusable-dashboard.yml index 14582f3..ede64d3 100644 --- a/.github/workflows/reusable-dashboard.yml +++ b/.github/workflows/reusable-dashboard.yml @@ -32,10 +32,6 @@ permissions: jobs: dashboard: runs-on: ubuntu-latest - environment: - name: github-pages - url: ${{ steps.deployment.outputs.page_url }} - steps: - name: Checkout Security Gate assets uses: actions/checkout@v4 @@ -81,17 +77,23 @@ jobs: path: reports/dashboard if-no-files-found: error - - name: Configure Pages - if: inputs.deploy_pages - uses: actions/configure-pages@v5 - - name: Upload Pages artifact if: inputs.deploy_pages uses: actions/upload-pages-artifact@v3 with: path: reports/dashboard + deploy-pages: + runs-on: ubuntu-latest + needs: dashboard + if: inputs.deploy_pages + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + steps: + - name: Configure Pages + uses: actions/configure-pages@v5 + - name: Deploy to GitHub Pages - if: inputs.deploy_pages id: deployment uses: actions/deploy-pages@v4 diff --git a/.github/workflows/security-baseline.yml b/.github/workflows/security-baseline.yml index 6abfba4..8f83eb4 100644 --- a/.github/workflows/security-baseline.yml +++ b/.github/workflows/security-baseline.yml @@ -19,4 +19,4 @@ jobs: mode: audit semgrep_config: auto repo_name: ${{ github.repository }} - deploy_pages: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }} + deploy_pages: false diff --git a/docs/onboarding.md b/docs/onboarding.md index 5d4c482..23a4cc0 100644 --- a/docs/onboarding.md +++ b/docs/onboarding.md @@ -50,7 +50,7 @@ jobs: semgrep_config: auto repo_name: ${{ github.repository }} gate_ref: v2.0.0 - deploy_pages: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }} + deploy_pages: false ``` ## Inputs @@ -96,3 +96,9 @@ Advanced users can call individual reusable workflows directly: - `.github/workflows/reusable-dashboard.yml` Use the product-level `reusable-security-gate.yml` unless you have a clear reason to compose the controls yourself. + +## GitHub Pages Deployment + +Dashboard artifacts are generated by default without deploying to Pages. Set `deploy_pages: true` only after the target repository has GitHub Pages configured and the `github-pages` environment protection rules are understood. + +If a run says "The deployment was rejected or didn't satisfy other protection rules", keep `deploy_pages: false` and use the `security-dashboard` artifact, or update the repository's `github-pages` environment approvals/branch rules before enabling Pages deployment. diff --git a/examples/security-gate-audit.yml b/examples/security-gate-audit.yml index b91e1eb..729bf09 100644 --- a/examples/security-gate-audit.yml +++ b/examples/security-gate-audit.yml @@ -21,4 +21,4 @@ jobs: semgrep_config: auto repo_name: ${{ github.repository }} gate_ref: v2.0.0 - deploy_pages: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }} + deploy_pages: false diff --git a/examples/security-gate-strict.yml b/examples/security-gate-strict.yml index 10e8124..70a3f3e 100644 --- a/examples/security-gate-strict.yml +++ b/examples/security-gate-strict.yml @@ -21,4 +21,4 @@ jobs: semgrep_config: auto repo_name: ${{ github.repository }} gate_ref: v2.0.0 - deploy_pages: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }} + deploy_pages: false From 24a3ffc08e7ba94da68d938034be0dc478440654 Mon Sep 17 00:00:00 2001 From: "Hel.Isa" Date: Sat, 2 May 2026 17:13:42 -0400 Subject: [PATCH 5/5] Add external company rollout guide to documentation --- README.md | 2 +- docs/external-company-rollout.md | 123 +++++++++++++++++++++++++++++++ 2 files changed, 124 insertions(+), 1 deletion(-) create mode 100644 docs/external-company-rollout.md diff --git a/README.md b/README.md index 45c2a31..d1a969b 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,7 @@ with: semgrep_config: auto ``` -See `docs/onboarding.md` for the full copy-paste workflow, `docs/presets.md` for preset guidance, and `docs/release-management.md` for stable tag usage. +See `docs/onboarding.md` for the full copy-paste workflow, `docs/presets.md` for preset guidance, `docs/release-management.md` for stable tag usage, and `docs/external-company-rollout.md` for repositories owned by another company. ## Repository Layout - `.github/workflows/` - orchestrator and reusable workflow modules diff --git a/docs/external-company-rollout.md b/docs/external-company-rollout.md new file mode 100644 index 0000000..2743303 --- /dev/null +++ b/docs/external-company-rollout.md @@ -0,0 +1,123 @@ +# External Company Rollout + +Use this guide when an approved repository belongs to another company or GitHub organization. + +## Recommended Options + +Choose one access model before adding the workflow to the target repository. + +| Option | Best for | Notes | +| --- | --- | --- | +| Public Security Gate repo | Open/shared baseline | The external company can call the reusable workflow directly. | +| Company-owned fork or mirror | Private customer/company use | The company owns the workflow copy and pins its own release tag. | +| Private same-organization repo | Internal use only | GitHub private reusable workflow sharing is designed for repositories in the same organization. | + +For another company, the most practical private model is a fork or mirror inside their GitHub organization. + +## Prerequisites + +1. Create and push a stable release tag in the Security Gate repo, for example `v2.0.0`. +2. Confirm the target repository allows GitHub Actions and reusable workflows. +3. Start with `deploy_pages: false` to avoid GitHub Pages environment protection issues. +4. Start with `mode: audit` so the first run reports findings without blocking the team. + +## If They Call This Repository Directly + +Use this only if `hel-isa/security-gate` is public or otherwise accessible to the target repository. + +Create `.github/workflows/security-gate.yml` in the external repository: + +```yaml +name: Security Gate + +on: + pull_request: + push: + branches: + - main + workflow_dispatch: + +permissions: + contents: read + pages: write + id-token: write + +jobs: + security-gate: + name: Security Gate + uses: hel-isa/security-gate/.github/workflows/reusable-security-gate.yml@v2.0.0 + with: + mode: audit + semgrep_config: auto + repo_name: ${{ github.repository }} + gate_repository: hel-isa/security-gate + gate_ref: v2.0.0 + deploy_pages: false +``` + +## If They Use Their Own Fork or Mirror + +This is the recommended private cross-company setup. + +1. Fork or mirror this repository into the company's GitHub organization. +2. Create the same stable release tag in that fork or mirror, for example `v2.0.0`. +3. Use the company-owned repository in both `uses` and `gate_repository`. + +```yaml +name: Security Gate + +on: + pull_request: + push: + branches: + - main + workflow_dispatch: + +permissions: + contents: read + pages: write + id-token: write + +jobs: + security-gate: + name: Security Gate + uses: COMPANY-ORG/security-gate/.github/workflows/reusable-security-gate.yml@v2.0.0 + with: + mode: audit + semgrep_config: auto + repo_name: ${{ github.repository }} + gate_repository: COMPANY-ORG/security-gate + gate_ref: v2.0.0 + deploy_pages: false +``` + +## First Run Checklist + +1. Open a pull request in the external repository. +2. Confirm the jobs run in audit mode. +3. Download the `security-dashboard` artifact. +4. Review Semgrep, Gitleaks, OSV, and SBOM output with the repository owner. +5. Add local allowlists or ignores only when the repository owner accepts the reason. +6. Switch to `mode: strict` only after findings are triaged. + +## Common Failures + +### Reusable workflow is not found + +Check that: + +- the Security Gate repo is public or accessible to the caller +- the tag exists, for example `v2.0.0` +- the workflow path is exactly `.github/workflows/reusable-security-gate.yml` + +### Dashboard asset checkout fails + +Check that `gate_repository` and `gate_ref` point to an accessible Security Gate repo and tag. + +### GitHub Pages deployment is rejected + +Keep `deploy_pages: false`. The dashboard artifact is still generated. Enable Pages only after the target repository's `github-pages` environment rules are configured. + +### Strict mode fails immediately + +Use `mode: audit` for onboarding. Strict mode is intentionally blocking when findings are detected.