Merge pull request #103 from SquidSec/harden/org-actions-github-hosted-ci

security: GitHub-hosted CI + SHA-pinned actions
This commit is contained in:
☣️ Mr. The Plague ☣️
2026-08-04 10:49:50 -05:00
committed by GitHub
4 changed files with 38 additions and 40 deletions
+27 -33
View File
@@ -6,10 +6,9 @@ on:
pull_request:
branches: [main, master]
# Self-hosted runners: labels [self-hosted, Linux|Windows, X64, squidsec]
# Avoid fixed host ports (8443) - concurrent jobs and lab stacks share the machine.
# SECURITY: never schedule untrusted fork PR code on self-hosted (CWE-284).
# Same-repo PRs and pushes only (private SquidSec runners).
# Public repo CI runs on GitHub-hosted runners (ubuntu-latest / windows-latest).
# Org self-hosted runners disallow public repos (RCE / LAN risk).
# SECURITY: same-repo PRs only for jobs that could touch secrets / heavy build.
concurrency:
group: ci-${{ github.workflow }}-${{ github.ref }}
@@ -19,25 +18,24 @@ jobs:
test:
# PRs: 3.12 only (faster). master/main push: 3.11 + 3.12 matrix.
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
runs-on: [self-hosted, Linux, X64, squidsec]
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version: ${{ github.event_name == 'pull_request' && fromJSON('["3.12"]') || fromJSON('["3.11", "3.12"]') }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
- name: Set up Python
uses: actions/setup-python@v5
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
with:
python-version: ${{ matrix.python-version }}
# Self-hosted: GitHub Actions pip cache download hangs (0 MB/s). Use local pip only.
cache: ""
- name: Install dependencies
run: |
set -euo pipefail
python -c "import sys; print(sys.executable, sys.version)"
python -m pip install --upgrade pip
# Same interpreter as tests (never bare `pip` on self-hosted)
# Same interpreter as tests
python -m pip install -r requirements-dev.txt
python -m pip install -e .
- name: Lint
@@ -62,11 +60,11 @@ jobs:
native-agent:
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
runs-on: [self-hosted, Linux, X64, squidsec]
runs-on: ubuntu-latest
needs: test
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
- uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5
with:
go-version: "1.25"
- name: Test + build sc5beacon matrix
@@ -92,7 +90,7 @@ jobs:
set -e
test "$code" -ne 0
grep -qi config /tmp/sc5beacon_err_$$.txt || grep -qi psk /tmp/sc5beacon_err_$$.txt || grep -qi required /tmp/sc5beacon_err_$$.txt
- uses: actions/upload-artifact@v4
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
continue-on-error: true
with:
name: sc5beacon-native
@@ -102,17 +100,17 @@ jobs:
docker:
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
runs-on: [self-hosted, Linux, X64, squidsec]
runs-on: ubuntu-latest
needs: test
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
- name: Build image
run: docker build -t "squidc5:ci-${{ github.run_id }}" .
- name: Run container smoke test
run: |
set -euo pipefail
NAME="squidc5-ci-${{ github.run_id }}-$$"
# Ephemeral host port - never bind fixed 8443 on shared runners
# Ephemeral host port
HOST_PORT=$(python3 -c 'import socket; s=socket.socket(); s.bind(("127.0.0.1",0)); print(s.getsockname()[1]); s.close()')
docker rm -f "$NAME" 2>/dev/null || true
docker run -d --name "$NAME" -p "127.0.0.1:${HOST_PORT}:8443" "squidc5:ci-${{ github.run_id }}"
@@ -141,14 +139,13 @@ jobs:
security:
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
runs-on: [self-hosted, Linux, X64, squidsec]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
- name: Set up Python
uses: actions/setup-python@v5
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
with:
python-version: "3.12"
# Self-hosted: GitHub Actions pip cache download hangs (0 MB/s). Use local pip only.
cache: ""
- name: Install pip-audit
run: |
@@ -158,7 +155,6 @@ jobs:
- name: Audit dependencies
run: |
set -euo pipefail
# Self-hosted can hit transient PyPI timeouts
for i in 1 2 3; do
# setup-python puts this env's scripts on PATH after python -m pip install
pip-audit -r requirements.txt && exit 0
@@ -176,18 +172,18 @@ jobs:
fail-fast: false
matrix:
include:
- runner: [self-hosted, Linux, X64, squidsec]
- runner: ubuntu-latest
artifact: linux-x64
is_windows: false
- runner: [self-hosted, Windows, X64, squidsec]
- runner: windows-latest
artifact: windows-x64
is_windows: true
runs-on: ${{ matrix.runner }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
- name: Set up Python
uses: actions/setup-python@v5
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
with:
python-version: "3.12"
cache: ""
@@ -207,9 +203,7 @@ jobs:
if: matrix.is_windows == true
shell: pwsh
run: |
# Bare `pip` on self-hosted Windows often targets a different Python than
# actions/setup-python. Always install via `python -m pip` so PyInstaller
# lands in the interpreter that runs packaging/build_binaries.py.
# Always install via `python -m pip` so PyInstaller matches this interpreter.
python -c "import sys; print(sys.executable)"
python -m pip install --upgrade pip
python -m pip install -r requirements.txt
@@ -266,7 +260,7 @@ jobs:
exit 1
- name: Upload binaries
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
continue-on-error: true
with:
name: squidc5-${{ matrix.artifact }}
@@ -285,14 +279,14 @@ jobs:
name: github-release
needs: [test]
if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master')
runs-on: [self-hosted, Linux, X64, squidsec]
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
- name: Set up Python
uses: actions/setup-python@v5
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
with:
python-version: "3.12"
cache: ""
@@ -322,7 +316,7 @@ jobs:
- name: Try Windows artifacts (optional)
continue-on-error: true
uses: actions/download-artifact@v4
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
with:
name: squidc5-windows-x64
path: artifacts/windows
+5 -5
View File
@@ -12,19 +12,19 @@ permissions:
jobs:
squidgate:
# Same-repo PRs only — never run fork workflow code on self-hosted
# Same-repo PRs only — do not run fork PR code with LLM_API_KEY
if: github.event.pull_request.head.repo.full_name == github.repository
runs-on: [self-hosted, Linux, X64, squidsec]
runs-on: ubuntu-latest
env:
LLM_API_KEY: ${{ secrets.LLM_API_KEY }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
with:
fetch-depth: 0
- name: SquidGate
if: env.LLM_API_KEY != ''
uses: SquidSec/SquidGate@v1.0.0-build.4
uses: SquidSec/SquidGate@5424e7343abbd7663bcc31920969f5a778ec8487 # v1.0.0-build.4
with:
llm-api-key: ${{ env.LLM_API_KEY }}
github-token: ${{ secrets.GITHUB_TOKEN }}
@@ -35,5 +35,5 @@ jobs:
- name: SquidGate not configured
if: env.LLM_API_KEY == ''
run: |
echo "::notice title=SquidGate::Add repository secret LLM_API_KEY to enable the PR security gate (SquidSec/SquidGate@v1.0.0-build.4)."
echo "::notice title=SquidGate::Add repository secret LLM_API_KEY to enable the PR security gate (SquidSec/SquidGate)."
+2
View File
@@ -74,6 +74,8 @@ merge main -> CI builds Linux/Windows binaries -> GitHub Release published
Releases: `https://github.com/SquidSec/SquidC5/releases` (created by CI job `github-release` on `master` only).
Public CI runs on **GitHub-hosted** runners (org self-hosted runners disallow public repositories). Prefer PRs into protected `master`.
- **Never** commit/push straight to `main`/`master`
- **Never** rsync WIP source or `docker compose up --build` to prod
- **Never** deploy a feature-branch-only binary
+4 -2
View File
@@ -36,8 +36,10 @@ ruff check src tests
## CI
- **CI** workflow: pytest (3.11/3.12), ruff, Docker smoke, pip-audit; Linux/Windows binaries + GitHub Release on push to `master`.
- Workflows run on **SquidSec self-hosted runners** and only schedule jobs for **same-repo** PRs (fork code is not executed on org runners). If Actions looks empty on a fork PR, that is expected — run the local checks above and note results in the PR.
- **SquidGate** (when configured): optional PR security gate via `SquidSec/SquidGate`. Repository secret `LLM_API_KEY` enables full analysis when available.
- **Public SquidC5 CI uses GitHub-hosted runners** (`ubuntu-latest` / `windows-latest`). Org self-hosted runners do not accept public repos.
- Actions are **SHA-pinned**; fork PR jobs that touch secrets are still gated to same-repo PRs. Fork contributors: run the local checks above and note results in the PR.
- **`master` is protected**: PR required, status checks (`test (3.12)`, `security`), no force-push.
- **SquidGate** (when configured): optional PR security gate. Repository secret `LLM_API_KEY` enables full analysis when available.
## Docs