Skip to content

fix: scope GitHub App token and deep-copy cached remote resources#2705

Open
theakshaypant wants to merge 2 commits intotektoncd:mainfrom
theakshaypant:fix/github-app-token-scoping/remote-tasks
Open

fix: scope GitHub App token and deep-copy cached remote resources#2705
theakshaypant wants to merge 2 commits intotektoncd:mainfrom
theakshaypant:fix/github-app-token-scoping/remote-tasks

Conversation

@theakshaypant
Copy link
Copy Markdown
Member

📝 Description of the Change

  • Scope GitHub App token to the triggering repository when no extra scope config is present, preventing remote task annotations from accessing private repos in the same installation
  • Deep-copy cached remote Pipeline and Task objects before inlining to prevent mutation from contaminating subsequent PipelineRuns that reference the same remote resource

🔗 Linked GitHub Issue

N/A

🧪 Testing Strategy

  • Unit tests
  • Integration tests
  • End-to-end tests
  • Manual testing
  • Not Applicable

Github App token scoping

  • Same github app installed in 2 private repos, akshay-pac-test-repo and jj-test
  • akshay-pac-test-repo pipelinerun references pipeline defined in jj-test.
  • Before fix: PipelineRun is able to access the remote task on the private jj-test without any extra config.
  • After the fix:
    with-fix
  • After the fix with repo configured in global PAC configmap
    image
$ kg cm pipelines-as-code -oyaml
apiVersion: v1
data:
  application-name: Pipelines as Code CI
  ...
  secret-github-app-scope-extra-repos: theakshaypant/jj-test
  secret-github-app-token-scoped: "false"
  ...
kind: ConfigMap
metadata:
  annotations:
    kubectl.kubernetes.io/last-applied-configuration: |
      ...
  labels:
    app.kubernetes.io/part-of: pipelines-as-code
    app.kubernetes.io/version: devel
  name: pipelines-as-code
  namespace: pipelines-as-code
  resourceVersion: "21512"
  uid: e118110b-2cda-446e-8f60-d087f6673bc0

Remote-pipeline Task Mutation

  • Same repository setup as above.
  • akshay-pac-test-repo defines 2 pipelineruns with the same remote pipeline def in jj-test
    image
  • second-run uses a local definition of my-task while first-run uses a remote definition.
  • Before fix:
    remote-mutation-without-fix
  • After fix:
    remote-mutation-with-fix

🤖 AI Assistance

AI assistance can be used for various tasks, such as code generation,
documentation, or testing.

Please indicate whether you have used AI assistance
for this PR and provide details if applicable.

  • I have not used any AI assistance for this PR.
  • I have used AI assistance for this PR.

Important

Slop will be simply rejected, if you are using AI assistance you need to make sure you
understand the code generated and that it meets the project's standards. you
need at least know how to run the code and deploy it (if needed). See
startpaac to make it easy
to deploy and test your code changes.

If the majority of the code in this PR was generated by an AI, please add a Co-authored-by trailer to your commit message.
For example:

Co-authored-by: Claude noreply@anthropic.com

✅ Submitter Checklist

  • 📝 My commit messages are clear, informative, and follow the project's How to write a git commit message guide. The Gitlint linter ensures in CI it's properly validated
  • ✨ I have ensured my commit message prefix (e.g., fix:, feat:) matches the "Type of Change" I selected above.
  • ♽ I have run make test and make lint locally to check for and fix any
    issues. For an efficient workflow, I have considered installing
    pre-commit and running pre-commit install to
    automate these checks.
  • 📖 I have added or updated documentation for any user-facing changes.
  • 🧪 I have added sufficient unit tests for my code changes.
  • 🎁 I have added end-to-end tests where feasible. See README for more details.
  • 🔎 I have addressed any CI test flakiness or provided a clear reason to bypass it.
  • If adding a provider feature, I have filled in the following and updated the provider documentation:
    • GitHub App
    • GitHub Webhook
    • Gitea/Forgejo
    • GitLab
    • Bitbucket Cloud
    • Bitbucket Data Center

@codecov-commenter
Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

❌ Patch coverage is 26.66667% with 11 lines in your changes missing coverage. Please review.
✅ Project coverage is 58.97%. Comparing base (6b69972) to head (9c0c99a).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
pkg/provider/github/github.go 0.00% 10 Missing and 1 partial ⚠️
❗ Your organization needs to install the Codecov GitHub app to enable full functionality.
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2705      +/-   ##
==========================================
- Coverage   59.00%   58.97%   -0.03%     
==========================================
  Files         208      208              
  Lines       20436    20446      +10     
==========================================
  Hits        12059    12059              
- Misses       7603     7612       +9     
- Partials      774      775       +1     

☔ View full report in Codecov by Sentry.
📢 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.

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces resource deep-copying during remote resolution to prevent cache mutation when multiple PipelineRuns share the same remote pipeline or task. It also adds logic to scope GitHub App tokens to specific repositories when no explicit token is provided. Review feedback highlights a security concern where token scoping is bypassed for non-Pull Request events due to unpopulated repository IDs, and an inconsistency in the URL used for token generation that could affect GitHub Enterprise installations.

// If Global and Repo level configurations are not provided then lets not override the provider token.
if token != "" {
event.Provider.Token = token
} else if len(v.RepositoryIDs) > 0 {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

security-high high

The scoping logic here relies on v.RepositoryIDs being populated, but this slice is currently only populated for Pull Request events (within populateRunEventFromPullRequest). For other event types, such as Push, v.RepositoryIDs remains empty. Consequently, the else if block is skipped, and the token remains unscoped (having access to all repositories in the installation). This defeats the security objective of the PR for non-PR event types.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Updated in 641eadc

// Token is scoped to only the calling repo if no additional scoping repos are configured
// so that no unwanted remote tasks are executed.
ns := info.GetNS(ctx)
scopedToken, err := v.GetAppToken(ctx, run.Clients.Kube, event.Provider.URL, event.InstallationID, ns)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

There is an inconsistency in the URL passed to GetAppToken. This call uses event.Provider.URL (the API URL, e.g., https://api.github.com/ or https://ghe.example.com/api/v3), whereas the initial token generation in InitAppClient (line 191) uses event.GHEURL (the base URL). For GitHub Enterprise installations, this discrepancy may lead to authentication failures or incorrect API endpoint resolution depending on how the underlying GetAppToken implementation handles the host/URL argument.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

@gemini-code-assist event.GHEURL and event.Provider.URL values are set to be same in

  • buildEventFromPipelineRun
  • ParsePayload

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Thank you for the clarification. While the values are currently set to be the same in those specific functions, using event.Provider.URL in GetAppToken introduces a dependency on the provider's API URL for token generation, whereas event.GHEURL is explicitly intended for base URL resolution. If event.Provider.URL is ever modified or configured differently (e.g., for specific API sub-paths), this could lead to authentication issues. Using event.GHEURL is safer and more consistent with the existing implementation in InitAppClient.

@theakshaypant theakshaypant marked this pull request as draft April 24, 2026 11:53
Reissue a scoped token in SetClient when no extra scope config is
present. The initial token is created before RepositoryIDs are
populated, so it has access to all repos in the installation. After
ScopeTokenToListOfRepos returns empty, fall back to GetAppToken
which now uses the populated RepositoryIDs.

Signed-off-by: Akshay Pant <akpant@redhat.com>
Use DeepCopy when reusing cached Pipeline and Task objects across
PipelineRuns. Without this, inlineTasks mutates the cached
original, contaminating subsequent runs that reference the same
remote pipeline.

Assisted-by: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Akshay Pant <akpant@redhat.com>
@theakshaypant theakshaypant force-pushed the fix/github-app-token-scoping/remote-tasks branch from 9c0c99a to bfb63c6 Compare April 27, 2026 04:09
@theakshaypant theakshaypant marked this pull request as ready for review April 27, 2026 04:10
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