Skip to content

fix(ci): drop unused write scopes from the xtest xct job (DSPX-4847) - #610

Merged
dmihalcik-virtru merged 1 commit into
mainfrom
DSPX-4847-harden-actions-token-permissions
Sep 18, 2026
Merged

dmihalcik-virtru merged 1 commit into
mainfrom
DSPX-4847-harden-actions-token-permissions

Conversation

@dmihalcik-virtru

@dmihalcik-virtru dmihalcik-virtru commented Sep 17, 2026 •

Copy link
Copy Markdown
Member

What

xct declares two write scopes it never uses:

      checks: write        # Needed to publish junit tests
      pull-requests: write # Add comments to PRs

Both comments are stale. This PR removes them, leaving contents: read (checkout) and packages: read (ghcr pulls via start-up-with-containers).

Why they're dead

  • No github.token, GITHUB_TOKEN, github-script, gh CLI, or API call of any kind anywhere in the xct job body (xtest.yml:329-833). The only secrets it references are BUF_TOKEN and PERSONAL_ACCESS_TOKEN_OPENTDF, neither of which is governed by these scopes.
  • None of the three composite actions it invokes touch the token either: xtest/setup-cli-tool/action.yaml, opentdf/platform/test/start-up-with-containers, opentdf/platform/test/start-additional-kas.
  • PR commenting lives in the separate publish-results job (:1437), which holds its own pull-requests: write. That job is untouched here.
  • There is no junit publisher anywhere in this file, so checks: write has nothing to publish.

Why it's worth doing

On a workflow_call the token xct holds is the caller's, and a called workflow's declared permissions force the caller to raise its ceiling to match. So these two scopes are granted to every downstream repo that calls xtest — currently opentdf/web-sdk, where they're the only thing preventing a tight caller-side permissions block (that's PR B of DSPX-4847).

And xct is a poor place to hold them: eight concurrent matrix legs, each building and running Go, Java, and JS dependency trees plus buf, uv, maven, and npm.

Testing

Self-testing — xtest.yml runs on: pull_request, so this PR's own xtest run executes the modified workflow from this ref. If anything in xct actually needed either scope, this PR goes red.

To confirm the scopes are really gone, check any xct leg's Set up job log: Checks and PullRequests should now read none. publish-results should still comment on this PR as usual.

actionlint is clean on the file (the SC2086 infos at :616/:634 are pre-existing and untouched).

Not included

Deliberately scoped to the two dead scopes:

  • The xtest capstone job (:1479) could go from contents: read to {} — it only echoes to $GITHUB_STEP_SUMMARY — but that's a separate, smaller argument.
  • PERSONAL_ACCESS_TOKEN_OPENTDF (:556) is a long-lived user-scoped PAT passed as BUF_INPUT_HTTPS_PASSWORD into make. It's empty on every workflow_call run (no secrets: block in the workflow_call contract) and Prepare java cli still succeeds, which suggests it may buy nothing — but it's populated on this repo's own runs, so that needs confirming separately before removal.

Tracked as DSPX-4847.

Summary by CodeRabbit

  • Security
    • Restricted automated workflow permissions to read-only access for repository contents and packages.
    • Removed write access for checks and pull requests from the test workflow.

@dmihalcik-virtru
dmihalcik-virtru requested review from a team as code owners September 17, 2026 19:46
@coderabbitai

coderabbitai Bot commented Sep 17, 2026 •

Copy link
Copy Markdown

Review Change StackReview Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: 0bba12b3-86db-4160-a0d7-5f995a124d6b

📥 Commits

Reviewing files that changed from the base of the PR and between 5344548 and 207ea13.

📒 Files selected for processing (1)
  • .github/workflows/xtest.yml

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.


📝 Walkthrough

Walkthrough

The xct job now uses read-only contents and packages permissions. Its checks: write and pull-requests: write permissions were removed.

Changes

Permissions hardening

Layer / File(s) Summary
Narrow xct token permissions
.github/workflows/xtest.yml
The xct job documents its read-only token scope and retains only contents: read and packages: read permissions.

Priority: ⬇️ Low

Estimated code review effort: 1 (Trivial) | ~2 minutes

Change: Other

Suggested reviewers: elizabethhealy

Merge Risk: ⚪ Minimal · up to 207ea

The permission reduction preserves xct’s required read access and does not affect pull-request result comments.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: removing unused write permissions from the CI xct job.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 0…
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

A rabbit checks the workflow gate
Read-only paws now set the state
Checks and pull writes hop away
Contents and packages guide the day
The token rests, both still and bright

Comment @coderabbitai help to get the list of available commands.

@dmihalcik-virtru

Copy link
Copy Markdown
Member Author

Verified on this PR's own run (35266888678) — all green.

The scopes are actually gone. Set up job on xct (main, go@main):

##[group]GITHUB_TOKEN Permissions
Contents: read
Metadata: read
Packages: read
##[endgroup]

No Checks, no PullRequests.

Nothing needed them. All 10 xct legs passed — go/java/js against both main and v0.26.0, including the released-pair combinations. If any step in the job had been quietly relying on either scope, this run is where it would have failed.

PR commenting still works. publish-results → success, and it commented on this PR. That job keeps its own pull-requests: write; this change doesn't touch it.

bench and zip64 skipped as expected (nightly cron only).

Note for whoever reviews: spec/DSPX-4847.md in this PR still has prs: []. I'm holding that one-line update rather than re-triggering the full 10-leg matrix for a metadata change — I'll fold it in with any review feedback.

The xct job declares checks: write and pull-requests: write, but uses
neither. There is no github.token, GITHUB_TOKEN, github-script, gh CLI or
API call anywhere in the job, and none of the composite actions it invokes
touch the token either. PR comments are posted by publish-results, which
holds its own pull-requests: write; no junit publisher exists in this file.

The scopes are not inert. On a workflow_call the token xct holds belongs to
the *calling* repository, so every caller is forced to raise its ceiling to
match -- and xct is eight concurrent matrix legs each building and running
Go, Java and JS dependency trees plus buf, uv, maven and npm. That is a lot
of third-party code sitting next to a token that can write checks and PR
comments on someone else's repo.

Signed-off-by: Dave Mihalcik <dmihalcik@virtru.com>
@dmihalcik-virtru
dmihalcik-virtru force-pushed the DSPX-4847-harden-actions-token-permissions branch from 5344548 to 207ea13 Compare September 17, 2026 21:43
@sonarqubecloud

Copy link
Copy Markdown

@dmihalcik-virtru
dmihalcik-virtru merged commit ef3fa4f into main Sep 18, 2026
22 checks passed
@dmihalcik-virtru
dmihalcik-virtru deleted the DSPX-4847-harden-actions-token-permissions branch September 18, 2026 12:09
dmihalcik-virtru added a commit to opentdf/web-sdk that referenced this pull request Sep 18, 2026
The caller granted no explicit permissions, so every job in the called
workflow ran with the repo default: Contents, Packages, Actions, Checks,
PullRequests, SecurityEvents, Statuses, Deployments, Issues and Pages all
write. That token sat beside 'npm ci' in lib, cli and web-app, and beside the
Go/Java/JS dependency builds in the called xtest workflow.

Declare the ceiling at the calling job instead. A called workflow can only
narrow a caller's grant, so this bounds the whole tree at contents: read,
packages: read and pull-requests: write.

opentdf/tests#610 had to land first: xtest's xct job declared checks: write
and pull-requests: write that it never used, and a callee asking for more
than the caller grants fails the call. With that merged, checks: write is no
longer needed anywhere here; pull-requests: write stays for xtest's
publish-results job, which comments coverage on the PR.

This file is the caller, so it runs from the PR ref and the change tests
itself. Per-job blocks inside reusable_build-and-test.yaml would narrow this
further but that file is pinned @main and cannot be validated from a PR;
tracked separately.

Signed-off-by: Dave Mihalcik <dmihalcik@virtru.com>
dmihalcik-virtru added a commit to opentdf/web-sdk that referenced this pull request Sep 18, 2026
* fix(ci): scope down the build-and-test GITHUB_TOKEN (DSPX-4847)

The caller granted no explicit permissions, so every job in the called
workflow ran with the repo default: Contents, Packages, Actions, Checks,
PullRequests, SecurityEvents, Statuses, Deployments, Issues and Pages all
write. That token sat beside 'npm ci' in lib, cli and web-app, and beside the
Go/Java/JS dependency builds in the called xtest workflow.

Declare the ceiling at the calling job instead. A called workflow can only
narrow a caller's grant, so this bounds the whole tree at contents: read,
packages: read and pull-requests: write.

opentdf/tests#610 had to land first: xtest's xct job declared checks: write
and pull-requests: write that it never used, and a callee asking for more
than the caller grants fails the call. With that merged, checks: write is no
longer needed anywhere here; pull-requests: write stays for xtest's
publish-results job, which comments coverage on the PR.

This file is the caller, so it runs from the PR ref and the change tests
itself. Per-job blocks inside reusable_build-and-test.yaml would narrow this
further but that file is pinned @main and cannot be validated from a PR;
tracked separately.

Signed-off-by: Dave Mihalcik <dmihalcik@virtru.com>

* pithify comments

* Remove 'what are github workflow permissions' comment

---------

Signed-off-by: Dave Mihalcik <dmihalcik@virtru.com>
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