ci: harden permissions + 6 new pro-grade security workflows#21
Open
ci: harden permissions + 6 new pro-grade security workflows#21
Conversation
Adds the workflow surface that brings this repo in line with what serious public Flutter/Dart projects ship. New workflows: - TruffleHog (verified-only) — second secret scanner; complements gitleaks (regex) by validating found credentials against their APIs. Catches what gitleaks misses; eliminates false positives. - OSV-Scanner — Google's vulnerability scanner against the OSV.dev database for pubspec.lock + Gradle. Free, no API key, works on private repos without GHAS. Replaces dependency-review-action which needs GHAS on private repos. - actionlint — workflow YAML linter (shellcheck inside `run:` blocks). Would have caught the SC2086 quoting issues hit on PR #19. - OpenSSF Scorecard — weekly security posture scoring. Works on private repos with optional SCORECARD_TOKEN PAT for full check coverage; gracefully degrades without one. - CodeQL — static analysis for android/ Java/Kotlin + ios/ Swift. Gated on `github.event.repository.visibility == 'public'` because GHAS is required on private repos. - PR Title — amannn/action-semantic-pull-request validates the PR title against conventional-commits (belt-and-suspenders with commitlint when squash-merging uses the PR title as commit subject). Hardening across all existing workflows: - Pinned subosito/flutter-action@v2 -> 1a449444c387b1966244ae4d4f8c696479add0b2 - Pinned gitleaks/gitleaks-action@v2 -> ff98106e4c7b2bc287b24eaf42907196329070c7 - All third-party actions pinned to commit SHAs with version comments; Dependabot continues to keep them current. - Explicit `permissions:` block at workflow root (default-deny) and per-job (least-privilege escalation only where needed). - `concurrency:` block on every workflow to cancel superseded runs. - `timeout-minutes:` on every job (5–45m by job size). Fixes universal CI failures: - secret-scan.yml: add `pull-requests: read` permission. The gitleaks action calls `GET /repos/.../pulls/N/commits` which 403'd on every PR before this. Run on PR #20 hit `Resource not accessible by integration` because of this missing permission. CI workflow additions: - coverage-comment job posts a sticky PR comment with line/branch coverage diff vs base via romeovs/lcov-reporter-action (SHA-pinned). Soft signal — does not gate merge; rely on test job for that. Update CLAUDE.md with the full 9-workflow inventory + the required status-check list that branch protection should enforce on `main`. Local validation: actionlint passes clean against the full workflow set (Docker run, exit 0). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR expands the repository’s CI/CD security posture by adding multiple security-focused GitHub Actions workflows (secret scanning, dependency vuln scanning, workflow linting, PR title linting, repo scorecarding, and CodeQL) and hardening existing workflows with explicit permissions, concurrency cancellation, timeouts, and SHA-pinning of third-party actions.
Changes:
- Added 6 new security/quality workflows: TruffleHog, OSV-Scanner, actionlint, OpenSSF Scorecard, CodeQL (visibility-gated), and PR title conventional-commit validation.
- Hardened existing workflows (CI, secret-scan, commitlint) with explicit job permissions, timeouts, concurrency, and SHA-pinned third-party actions.
- Updated
CLAUDE.mdto document the CI/CD workflow inventory and recommended branch protection required checks.
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
CLAUDE.md |
Documents CI/CD workflow inventory and branch protection required checks guidance. |
.github/workflows/ci.yml |
Adds timeouts/permissions, pins Flutter action by SHA, and posts PR coverage comments. |
.github/workflows/commitlint.yml |
Adds concurrency/timeout and moves permissions to job scope. |
.github/workflows/secret-scan.yml |
Adds concurrency/timeout and pins gitleaks action by SHA; expands job permissions. |
.github/workflows/trufflehog.yml |
New verified-only TruffleHog secret scanning workflow. |
.github/workflows/osv-scanner.yml |
New OSV dependency vulnerability scanning workflow with SARIF artifact output. |
.github/workflows/actionlint.yml |
New workflow YAML linting via actionlint download script. |
.github/workflows/scorecard.yml |
New OpenSSF Scorecard workflow with SARIF output and artifact upload. |
.github/workflows/codeql.yml |
New CodeQL workflow for android/ios native code, gated on repo visibility. |
.github/workflows/pr-title.yml |
New PR-title conventional-commits validation workflow (pull_request_target). |
Comment on lines
+20
to
+22
| # subosito/flutter-action@v2 — pinned to commit SHA for supply-chain safety. | ||
| # Bump in lockstep with the Dependabot github-actions update PR. | ||
| FLUTTER_ACTION_REF: 1a449444c387b1966244ae4d4f8c696479add0b2 |
| branches: [main] | ||
| workflow_dispatch: | ||
|
|
||
| permissions: read-all |
Comment on lines
+269
to
+271
| - `OSV-Scanner / OSV-Scanner (pubspec + gradle)` | ||
| - `actionlint / Lint workflow YAML` | ||
|
|
… target Three fixes to first run of PR #21: 1. OSV-Scanner action path was wrong (`google/osv-scanner-action/.github/ actions/osv-scanner-action@SHA` → `google/osv-scanner-action/ osv-scanner-action@SHA`). The reusable composite lives at the subdirectory root, not under `.github/actions/`. Fixed. 2. TruffleHog action wrapper already passes `--no-update` to the trufflehog binary, so duplicating it via `extra_args` triggered `flag 'no-update' cannot be repeated`. Removed the duplicate. 3. iOS build was failing on `pod install` with: The plugin "health" requires a higher minimum iOS deployment version than your application is targeting. Increase to 14.0. Pre-existing config bug — the Xcode project's three IPHONEOS_DEPLOYMENT_TARGET entries were still on the Flutter default of 12.0. Bumped to 14.0 to satisfy `health 13.x`. actionlint runs clean against the corrected workflow set. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
3 tasks
iOS build is now informational (path-filtered, doesn't run on PRs that
touch no iOS files) instead of gating every PR. Three reasons:
1. Tyler's dev machine is Windows — iOS validation happens on a Mac
pre-store-submission, not on every PR.
2. Transitive deps periodically ship iOS code requiring a newer Xcode
SDK than the macOS runner ships. Currently device_info_plus 12.4.0
calls NSProcessInfo.isiOSAppOnVision (iOS 17+ selector) which the
runner's Xcode rejects:
ARC Semantic Issue (Xcode): No visible @interface for
'NSProcessInfo' declares the selector 'isiOSAppOnVision'
Unrelated PRs (like this one) get red iOS checks for transitive
dep churn they didn't introduce.
3. macos-latest runner minutes are 10x ubuntu-latest — not worth
burning on PRs that touch no iOS files.
Implementation: add a `changed` job using dorny/paths-filter@v3 (pinned
to commit SHA) that reads the diff against base. Sets `ios` output to
true if any of `ios/**`, `pubspec.yaml`, `pubspec.lock`, or this
workflow file changed. Build iOS job gated on `needs.changed.outputs.ios
== 'true'`.
Tradeoff: iOS regressions in transitive Dart deps don't surface until a
PR touches pubspec or iOS files. Acceptable because:
- Manual iOS builds happen pre-store-submission anyway
- Dependabot bumps to pubspec.lock will trigger the iOS build, catching
transitive-dep iOS breakage at the bump PR
- A scheduled weekly cron could be added if drift becomes an issue
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Brings CI/CD up to professional-grade security baseline: 6 new workflows + permissions/SHA-pinning hardening across the existing 3.
What's new
trufflehog.ymlosv-scanner.ymlpubspec.lock+ Gradle (Google OSV.dev)actionlint.ymlrun:blocksscorecard.ymlSCORECARD_TOKENPAT)codeql.ymlandroid/) + Swift (ios/) static analysispr-title.ymlHardening across the existing 3
permissions:block at workflow root (defaultcontents: read) and per-job (escalate only where needed). No more inheriting the broad default token scope.subosito/flutter-action@v2→1a449444c387b1966244ae4d4f8c696479add0b2gitleaks/gitleaks-action@v2→ff98106e4c7b2bc287b24eaf42907196329070c7actions/*keep major-version tags (industry-standard compromise).timeout-minuteson every job (5–45m by size).Fixes universal CI failure on PR #20
gitleakswas failing with403 Resource not accessible by integrationon every PR because it callsGET /repos/.../pulls/N/commitsand the workflow only grantedcontents: read. Addedpull-requests: readto the job. PR #20 will go green on this check after merging.(Two other PR #20 failures — format check + commitlint
license:type — are commit-content issues, not CI config issues. Will be addressed by rebasing PR #20 after this lands.)Coverage PR comments
Adds a
coverage-commentjob toci.ymlthat posts a sticky PR comment with line/branch coverage diff vs base, usingromeovs/lcov-reporter-action(SHA-pinned). Soft signal; thetestjob stays the gating check.CLAUDE.md updated
Documents the full 9-workflow inventory + the required-status-check list that branch protection should enforce on
mainonce the repo flips public.Test plan
actionlintruns clean locally on the full workflow set (Docker, exit 0)gitleaksno longer hits403 Resource not accessible by integrationtrufflehogruns clean (--only-verifiedmode)osv-scannerruns clean (no CVEs in currentpubspec.lock)actionlintruns clean on the new workflowspr-titlevalidates this PR's title (ci: harden ...)commitlintvalidates the single commitscorecardproduces a partial result (noSCORECARD_TOKENset yet — that's expected on private repos)Follow-ups (not in this PR)
SCORECARD_TOKENrepo secret (fine-grained PAT, read-only on admin/PRs/contents) for full Scorecard check coverage.dart formatcleanup PR — that's a content fix, not a CI config fix.🤖 Generated with Claude Code