Skip to content

workflows: least-privilege permissions and secrets for run-tests.yml - #4285

Open
fabwa wants to merge 3 commits into
nspcc-dev:masterfrom
fabwa:fix/ci-least-privilege-run-tests
Open

fabwa wants to merge 3 commits into
nspcc-dev:masterfrom
fabwa:fix/ci-least-privilege-run-tests

Conversation

@fabwa

@fabwa fabwa commented Nov 7, 2026

Copy link
Copy Markdown
Contributor

Summary

run-tests.yml granted the job-wide write-all token and forwarded every repository secret (secrets: inherit) to a reusable workflow it calls in a different repository (nspcc-dev/neofs-testcases's system-tests.yml).

I checked that workflow's own definition: it declares exactly two secrets (TEST_RESULTS_WALLET, TEST_RESULTS_PASSWORD) and its only use of GITHUB_TOKEN is guibranco/github-status-action-v2 posting a commit status, which needs only the statuses: write permission. Everything else write-all grants, and every other repository secret secrets: inherit forwards, is unused by the callee.

Fix

Narrow both to exactly what's needed: permissions: statuses: write, and an explicit secrets: block naming just the two secrets the callee declares. GitHub Actions treats any unlisted permission as none once permissions is a map rather than write-all/read-all, so this is a straightforward reduction with no other behavioral change - this workflow's own steps don't do anything besides invoking the reusable workflow, so they need nothing beyond what it needs.

Left the @master refs (here and in go.yml/dco.yml) as a separate, broader question: pinning reusable-workflow references to a specific commit is good supply-chain hygiene in general, but introduces an ongoing maintenance cost (bumping the pin on every upstream workflow change) that's a policy call for the maintainers, not a scoped bug fix - and go.yml/dco.yml don't carry the write-all + secrets:inherit combination this fix addresses, so they're materially lower risk as-is.

Test plan

  • Verified with actionlint (clean across all workflow files in the repo).
  • Patch verified to apply cleanly in a separate clean worktree against the current master tip, and lints clean there too.
  • Real functional validation is this PR's own CI run, which exercises run_system_tests/run_all_tests exactly as configured here.

🤖 Generated with Claude Code

@codecov

codecov Bot commented Nov 7, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 32.71%. Comparing base (34d007f) to head (373faeb).
⚠️ Report is 1 commits behind head on master.

Additional details and impacted files
@@           Coverage Diff           @@
##           master    #4285   +/-   ##
=======================================
  Coverage   32.70%   32.71%           
=======================================
  Files         673      673           
  Lines       41698    41701    +3     
=======================================
+ Hits        13638    13641    +3     
  Misses      28060    28060           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@roman-khimov roman-khimov left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@evgeniiz321, please check and improve other repos if it works this way.

Fabian Wahle and others added 2 commits November 10, 2026 20:25
run-tests.yml granted the job-wide write-all token and forwarded every
repository secret (secrets: inherit) to a reusable workflow it calls
in a different repository (nspcc-dev/neofs-testcases's
system-tests.yml). Checking that workflow's own definition: it
declares exactly two secrets (TEST_RESULTS_WALLET,
TEST_RESULTS_PASSWORD) and its only use of GITHUB_TOKEN is
guibranco/github-status-action-v2 posting a commit status, which needs
only the statuses: write permission. Everything else write-all grants,
and every other repository secret secrets: inherit forwards, is unused
by the callee.

Narrow both to exactly what's needed: permissions: statuses: write,
and an explicit secrets: block naming just the two secrets the callee
declares. GitHub Actions treats any unlisted permission as none once
permissions is a map rather than write-all/read-all, so this is a
straightforward reduction with no other behavioral change - the
workflow's own steps don't do anything besides invoking the reusable
workflow, so they need nothing beyond what it needs.

Left the @master refs (here and in go.yml/dco.yml) as a separate,
broader question: pinning reusable-workflow references to a specific
commit is good supply-chain hygiene in general, but introduces an
ongoing maintenance cost (bumping the pin on every upstream workflow
change) that's a policy call for the maintainers, not a scoped bug
fix - and go.yml/dco.yml don't carry the write-all + secrets:inherit
combination this fix addresses, so they're materially lower risk as-is.

Verified with actionlint (clean across all workflow files) since
there's no local way to execute a reusable-workflow call; the real
validation is this PR's own CI run, which exercises run_system_tests
exactly as configured here.

Signed-off-by: Fabian Wahle <fabian@nash.io>
A permissions block sets every unlisted scope to none, and
neofs-testcases/system-tests.yml checks out five repositories. Verified
against that workflow: it declares exactly two secrets (both required)
and sets no permissions of its own, and its only token use is two
commit-status posts.

Signed-off-by: Fabian Wahle <fabian@nash.io>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@fabwa
fabwa force-pushed the fix/ci-least-privilege-run-tests branch from f0b2d91 to ebb3f15 Compare November 10, 2026 19:34
Signed-off-by: Fabian Wahle <fabian@nash.io>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@fabwa

fabwa commented Nov 10, 2026

Copy link
Copy Markdown
Contributor Author

Note on the red run_system_tests here — it is evidence for the change, not against it.

nspcc-dev/neofs-testcases/.github/workflows/system-tests.yml declares exactly two secrets, both required: true: TEST_RESULTS_WALLET and TEST_RESULTS_PASSWORD (GITHUB_TOKEN is automatic for a called workflow), so the explicit list is complete.

With secrets: inherit, every PR from a fork dies before any test runs:

Error when evaluating 'secrets'. .github/workflows/run-tests.yml (Line: 42, Col: 11): Secret TEST_RESULTS_WALLET is required, but not provided while calling.

That is what the other PRs in this stack show. On this branch the call evaluates, the job starts and gets as far as the tests themselves (exit code 2) — the empty values satisfy the required-secret check instead of failing the call. Same-repo PRs are unaffected either way.

CHANGELOG entry added in 373faeb.

@fabwa

fabwa commented Nov 10, 2026

Copy link
Copy Markdown
Contributor Author

Second red here, neofs-node tests, is also not this PR — the commit it ran on adds one CHANGELOG line, and the failing package is pkg/local_object_storage/engine, which this branch does not touch:

panic: Log in goroutine after TestStorageEngine_GetBytes has completed:
  DEBUG  started expired objects handling  {"shard_id": "...", "epoch": 0}

Reproduced on unmodified 34d007fc3 with go test ./pkg/local_object_storage/engine/ -count=2 (it passes at -count=1, and picks a different victim test each time — here TestErrorReporting/ignore_errors_by_default).

Cause: newEngine in error_test.go calls e.Init(), which runs s.gc.init() per shard with zaptest.NewLogger(t), and never closes the engine — no defer, no t.Cleanup. Once the test returns, tickRemover keeps firing and collectExpiredObjects logs into the finished test's logger. Of the tests using that helper only TestErrorReporting has an e.Close(), at the end of one subtest.

Happy to send the t.Cleanup fix as its own PR if you want it.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants