Documents why no concurrency group is set in 22 PR-triggered workflows,
preventing well-meaning re-addition without addressing the underlying
trigger-interaction issue.
Two comment variants:
- workflow_call workflows (12): dual-trigger conflict — direct PR runs
and ci-gate/release-gate-called runs would share a key and cancel
each other mid-flight.
- PR-only workflows (10): cancel-in-progress was too aggressive,
killing runs before they produced useful results. Scheduled/push-only
triggers are safe candidates for future concurrency.
Ref #3554 (reverted in #3599).
Adds a `concurrency:` block to 22 PR-triggered workflows so that
successive pushes to the same PR cancel the previous run instead
of queueing. Previously only 2/55 workflows had concurrency
groups, meaning every push-push iteration doubled Actions minutes.
The group key uses `${{ github.head_ref || github.run_id }}`:
- On PR events, `head_ref` is the source branch — pushes to the
same PR share a group → second run cancels the first.
- On push/release/schedule events, `head_ref` is empty and falls
back to `run_id` (unique per run) — each run gets its own group
and is never cancelled.
This makes the pattern safe for workflows like
`backwards-compatibility.yml` that run on both PR and push:main —
main-branch signal is preserved, only PR-iteration runs are
cancellable.
Excluded by design:
- release.yml / publish.yml / docker-publish.yml — release
pipelines; mid-publish cancellation is destructive.
- ci-gate.yml / release-gate.yml — `workflow_call`-only,
cancellation propagates badly through nested workflows.
- dependency-review.yml — already has a concurrency block.
Updates the pinned SHA from 58077d3c7e43986b6b15fba718e8ea69e387dfcc
(v2.15.1) to fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 (v2.16.0) in all
53 workflow files (79 occurrences).
Resolves all 158 zizmor known-vulnerable-actions code scanning alerts.
Merge the separate security-gate, test-gate (Playwright WebKit), and
e2e-test-gate (Puppeteer) jobs from release.yml into a single
release-gate.yml workflow. All checks are now blocking — no more
advisory-only gates that silently pass on failure.
Key changes:
- Rename security-release-gate.yml → release-gate.yml
- Add playwright-webkit-tests job to the unified gate
- Add secrets declaration (OPENROUTER_API_KEY, SERPER_API_KEY)
- Remove test-gate and e2e-test-gate jobs from release.yml
- Update build job to depend only on release-gate
- Update all 19 child workflow comments to reference release-gate.yml
- Update README badge URL
Add push and pull_request triggers to catch security vulnerabilities
before they're merged to main, rather than only at release time.
This provides shift-left security - vulnerabilities like path injection
(CWE-22) will now be flagged during PR review instead of being
discovered days later by scheduled scans.
Security workflows were running on every push to main, causing GitHub
Actions rate limiting. Per the security gate philosophy, these scans
should run at the release gate (via workflow_call) rather than on every
push - vulnerabilities can be fixed before release.
Removed push triggers from:
- codeql.yml (was: push to main/dev)
- devskim.yml (was: push to main/dev)
- bearer.yml (was: push to main/dev)
- zizmor-security.yml (was: push with workflow path filter)
- hadolint.yml (was: push with Dockerfile path filter)
- checkov.yml (was: push with Docker/IaC path filter)
- npm-audit.yml (was: push with package.json path filter)
- retirejs.yml (was: push with JS/TS path filter)
Preserved:
- workflow_call triggers (for security-release-gate integration)
- workflow_dispatch triggers (for manual runs)
- schedule triggers (for early warning and compliance audit trail)
- gitleaks push trigger (secrets in main = permanent exposure)
Move most security scans from PR-level to release gate only to reduce
PR CI time while maintaining release security.
Philosophy:
- Secrets (gitleaks) remain on PRs - secrets in main = permanent exposure
- All other scans run at release gate - issues can be fixed before release
Changes:
- Remove pull_request triggers from: codeql, checkov, devskim, hadolint,
zizmor-security, retirejs, npm-audit, osv-scanner
- Add workflow_call to all security workflows for release gate integration
- Update security-release-gate.yml to call 14 security scans in parallel:
- Core: security-tests, semgrep, container-security, owasp-zap
- SAST: codeql, devskim
- Dependencies: osv-scanner, npm-audit, retirejs
- Container/IaC: dockle, hadolint, checkov
- Workflow: zizmor
- Secrets: gitleaks
- Keep dependency-review on PRs only (requires PR context to compare changes)
Benefits:
- Faster PR CI (only gitleaks + non-security checks run)
- Same security coverage at release time
- All scans run in parallel (~25-30 min total)
- Remove --require-hashes from checkov pip install (fails due to transitive dependencies)
- Update codeql-action hash from tag object (32f7c15...) to actual commit (0499de3...)
in 8 workflows to resolve zizmor impostor-commit security alerts
This prevents Git credentials from being persisted in the checkout
directory, which could leak into artifacts (artipacked vulnerability).
Fixes zizmor/artipacked security alerts.
Resolved conflicts:
- .gitleaks.toml: Combined regex patterns from both branches, added path allowlists
- pyproject.toml: Kept updated versions from dev + added hypothesis from main
- __version__.py: Keep 1.3.0 from dev
- news.js: Removed duplicate toggleExpanded function (already exists at line 1291)
- pdm.lock: Regenerated with pdm lock
- Take dev version for __version__.py (1.3.0)
- Take dev version for package.json (updated deps & scripts)
- Combine best of both for ui.js (ldr- prefix + textContent for security)
Resolves 7 OSSF Scorecard HIGH severity alerts for token permissions.
## Changes
### Workflows Fixed:
1. codeql.yml - Removed job-level permissions, added packages:read and actions:read to top-level
2. container-security.yml - Removed job-level permissions from both jobs, added actions:read to top-level
3. devskim.yml - Removed job-level permissions, added actions:read to top-level
4. docker-publish.yml - Removed packages:write (not needed for Docker Hub)
5. gitleaks.yml - Removed job-level permissions, added actions:read to top-level
6. hadolint.yml - Removed job-level permissions, added pull-requests:write to top-level
7. semgrep.yml - Removed job-level permissions, added actions:read to top-level
## Problem
OSSF Scorecard flagged these workflows for having both top-level and job-level
permissions declarations. This pattern can lead to:
- Permission confusion and misconfiguration
- Potential privilege escalation if not carefully managed
- Violation of principle of least privilege best practices
## Solution
- Consolidated all required permissions at the top-level only
- Removed redundant job-level permission blocks
- Added necessary permissions (actions:read, packages:read, pull-requests:write) to top-level where needed
- Reduced docker-publish.yml from packages:write to contents:read (only publishing to Docker Hub, not GHCR)
- Added explanatory comments for each permission
## Security Impact
✅ Resolves 7 OSSF Scorecard HIGH severity alerts
✅ Implements principle of least privilege
✅ Reduces permission confusion across workflows
✅ Maintains all necessary functionality
✅ Consistent with GitHub security best practices
## Testing
- All workflows maintain same functionality with centralized permissions
- No changes to actual workflow logic or steps
- Permissions properly scoped to minimum requirements
Resolved conflicts:
- .gitleaks.toml: Merged commit allowlists from both branches
- pdm.lock: Accepted dev version (lock files should be regenerated)
- src/local_deep_research/security/file_write_verifier.py: Accepted dev version with settings_snapshot approach
- Add CodeQL configuration to exclude file_write_verifier.py from clear-text storage alerts
- This is a security-verified utility that only writes after authorization checks
- Update CodeQL workflow to use the configuration file