security(runtime): harden TLS provenance, lifecycle, and public error boundaries #15767
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: DAST smoke (PR) | |
| on: | |
| pull_request: | |
| branches: ["main", "release/**"] | |
| # Runner-cost guard (#8084): the CLI-bundle build alone is 6-11min; a docs-only PR | |
| # cannot change DAST behavior, so skip the whole workflow for pure docs/markdown | |
| # changes. Any code path in the diff still runs the full smoke. | |
| paths-ignore: | |
| - "docs/**" | |
| - "**/*.md" | |
| permissions: | |
| contents: read | |
| # Superseded runs on the same PR must not stack 25-minute advisory builds | |
| # (force-push storms were holding 2-3 runners each). Same group rule as quality.yml. | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| dast-smoke: | |
| runs-on: ubuntu-latest | |
| # ADVISORY while this new gate matures (repo convention: advisory -> blocking). | |
| # Flip to blocking (remove continue-on-error) once it's proven stable across a few PRs. | |
| continue-on-error: true | |
| # Build CLI bundle alone varies 6-11min on GitHub-hosted runners (3 consecutive | |
| # timeouts observed on 2026-07-14 with the old 12min cap killing schemathesis | |
| # mid-run) — 25min leaves real headroom for the actual DAST steps. | |
| timeout-minutes: 25 | |
| env: | |
| JWT_SECRET: ci-build-secret-with-sufficient-length-for-validation | |
| API_KEY_SECRET: ci-api-key-secret-with-sufficient-length-aaaa | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| persist-credentials: false | |
| - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 | |
| with: | |
| node-version: "24" | |
| cache: npm | |
| - uses: ./.github/actions/npm-ci-retry | |
| - name: Build CLI bundle | |
| env: | |
| OMNIROUTE_BUILD_BACKEND_ONLY: "1" | |
| run: npm run build:cli | |
| - name: Start OmniRoute | |
| env: | |
| PORT: "20128" | |
| INJECTION_GUARD_MODE: block | |
| REQUIRE_API_KEY: "false" | |
| run: | | |
| node dist/server.js > server.log 2>&1 & | |
| echo $! > server.pid | |
| for _ in $(seq 1 30); do | |
| if curl -sf http://localhost:20128/api/monitoring/health >/dev/null; then echo up; break; fi | |
| sleep 2 | |
| done | |
| - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 | |
| with: | |
| python-version: "3.12" | |
| - run: pip install schemathesis | |
| - name: Schemathesis smoke (high-risk endpoints, blocking) | |
| run: | | |
| # /api/auth/oidc/* is a BROWSER redirect flow (302 to the IdP, 302 back to | |
| # /login?oidc_error=... on every failure), not a REST endpoint: Schemathesis reads | |
| # those 302s as "the API accepted a schema-violating request" and the configured-off | |
| # 400 as "rejected a schema-compliant request". Documenting the flow in the spec is | |
| # still right (operators need it); fuzzing it is not what this smoke is for. | |
| # /api/auth/login has brute-force rate limiting: repeated failed logins return 429, | |
| # which Schemathesis flags as rejection of schema-compliant requests. | |
| schemathesis run docs/openapi.yaml --url http://localhost:20128 \ | |
| --include-path-regex '^/v1/(chat/completions|models)$|^/api/(auth|keys)' \ | |
| --exclude-path-regex '^/api/auth/(oidc/|login)' \ | |
| --max-examples 8 --workers 4 --checks all --max-response-time 30 \ | |
| --request-timeout 20 --suppress-health-check all --no-color | |
| - name: Install promptfoo | |
| run: npm install -g promptfoo@0.122.0 | |
| - name: promptfoo injection-guard (blocking) | |
| env: | |
| OMNIROUTE_URL: http://localhost:20128 | |
| OMNIROUTE_API_KEY: not-needed-blocked-before-upstream | |
| run: promptfoo eval -c promptfooconfig.yaml --no-cache | |
| - name: Stop server | |
| if: always() | |
| run: kill "$(cat server.pid)" || true | |
| - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| if: always() | |
| with: | |
| name: dast-smoke-logs | |
| path: server.log | |
| retention-days: 7 |