For AI agents: A markdown version of this page is available at https://docs.datadoghq.com/security/code_security/iac_security/iac_rules/cicd-github-use-trusted-publishing.md.
A documentation index is available at /llms.txt.
Workflows that publish packages should use Trusted Publishing (GitHub OIDC) instead of embedding long-lived tokens or other manually configured credentials. Manual credentials increase the risk of leakage, reuse across projects, and supply-chain compromise. This rule flags publishing steps that supply explicit credentials or indicate manual token use, and run steps that invoke publish commands without OIDC id-token permissions.
For uses steps, known publishing actions are flagged — such as pypa/gh-action-pypi-publish, rubygems/release-gem, rubygems/configure-rubygems-credentials, and actions/setup-node — when with contains keys like password, api-token, or always-auth set to true, or when with.setup-trusted-publisher is not set to true. The rule also checks with.repository-url / with.repository_url and with.registry-url against known trusted indices to determine intent.
For run steps, commands that match publishing operations are flagged — such as cargo publish, twine upload, npm publish, gem push, and dotnet nuget push — when the parent job does not grant the OIDC id-token permission (e.g., permissions.id-token: write). Resources missing the appropriate OIDC permissions or containing the listed insecure with values will be flagged. Remediate by removing hardcoded tokens/credentials and configuring the job/actions to rely on id-token or the action’s Trusted Publishing option.
Secure example:
jobs:publish:permissions:id-token:writesteps:- name:Publish to PyPIuses:pypa/gh-action-pypi-publish@v1with:repository-url:https://upload.pypi.org/legacy/
Compliant Code Examples
name:Trusted Publishing Test - Negative Caseson:push:branches:[main]jobs:pypi-trusted-publishing:runs-on:ubuntu-latestpermissions:id-token:writesteps:- uses:actions/checkout@v4- name:Publish to PyPI with Trusted Publishinguses:pypa/gh-action-pypi-publish@release/v1# No password - uses OIDC tokenrubygems-trusted-publishing:runs-on:ubuntu-latestpermissions:id-token:writesteps:- uses:actions/checkout@v4- name:Release gem with trusted publishinguses:rubygems/release-gem@v1with:setup-trusted-publisher:truenpm-trusted-publishing:runs-on:ubuntu-latestpermissions:id-token:writesteps:- uses:actions/checkout@v4- uses:actions/setup-node@v4with:node-version:20registry-url:https://registry.npmjs.org# No always-auth, will use provenancecargo-with-token:runs-on:ubuntu-latestpermissions:id-token:writesteps:- uses:actions/checkout@v4- name:Publish to crates.io with token permissionrun:cargo publishdry-run-commands:runs-on:ubuntu-lateststeps:- uses:actions/checkout@v4- name:Dry run cargo publishrun:cargo publish --dry-run- name:Dry run npm publishrun:npm publish --dry-run- name:Dry run poetry publishrun:poetry publish --dry-runnon-publishing-commands:runs-on:ubuntu-lateststeps:- uses:actions/checkout@v4- name:Build with cargorun:cargo build --release- name:Run testsrun:npm test- name:Install with gemrun:gem install bundlerdifferent-registries:runs-on:ubuntu-lateststeps:- uses:actions/checkout@v4# Private registry, not npmjs.org- uses:actions/setup-node@v4with:node-version:20registry-url:https://npm.pkg.github.comalways-auth:truerubygems-private-server:runs-on:ubuntu-lateststeps:- uses:actions/checkout@v4# Private gem server, not rubygems.org- name:Configure private gem serveruses:rubygems/configure-rubygems-credentials@mainwith:api-token:${{ secrets.PRIVATE_GEM_TOKEN }}gem-server:https://private-gems.example.comworkflow-level-permissions:runs-on:ubuntu-lateststeps:- uses:actions/checkout@v4- name:Publish with workflow-level tokenrun:twine upload dist/*permissions:id-token:write
Non-Compliant Code Examples
name:Trusted Publishing Test - Positive Caseson:push:branches:[main]jobs:pypi-manual-password:runs-on:ubuntu-lateststeps:- uses:actions/checkout@v4- name:Publish to PyPI with passworduses:pypa/gh-action-pypi-publish@release/v1with:password:${{ secrets.PYPI_API_TOKEN }}rubygems-no-trusted:runs-on:ubuntu-lateststeps:- uses:actions/checkout@v4- name:Release gem without trusted publishinguses:rubygems/release-gem@v1rubygems-disabled-trusted:runs-on:ubuntu-lateststeps:- uses:actions/checkout@v4- name:Release gem with trusted publishing disableduses:rubygems/release-gem@v1with:setup-trusted-publisher:falserubygems-manual-token:runs-on:ubuntu-lateststeps:- uses:actions/checkout@v4- name:Configure RubyGems with manual tokenuses:rubygems/configure-rubygems-credentials@mainwith:api-token:${{ secrets.RUBYGEMS_API_KEY }}gem-server:https://rubygems.orgnpm-manual-auth:runs-on:ubuntu-lateststeps:- uses:actions/checkout@v4- uses:actions/setup-node@v4with:node-version:20registry-url:https://registry.npmjs.orgalways-auth:truecargo-publish-no-token:runs-on:ubuntu-lateststeps:- uses:actions/checkout@v4- name:Publish to crates.iorun:cargo publishtwine-upload-no-token:runs-on:ubuntu-lateststeps:- uses:actions/checkout@v4- name:Publish to PyPI with twinerun:twine upload dist/*npm-publish-no-token:runs-on:ubuntu-lateststeps:- uses:actions/checkout@v4- name:Publish to npmrun:npm publishgem-push-no-token:runs-on:ubuntu-lateststeps:- uses:actions/checkout@v4- name:Push gemrun:gem push mygem-1.0.0.gempoetry-publish-no-token:runs-on:ubuntu-lateststeps:- uses:actions/checkout@v4- name:Publish with poetryrun:poetry publishyarn-publish-no-token:runs-on:ubuntu-lateststeps:- uses:actions/checkout@v4- name:Publish with yarnrun:yarn publishpnpm-publish-no-token:runs-on:ubuntu-lateststeps:- uses:actions/checkout@v4- name:Publish with pnpmrun:pnpm publishnuget-push-no-token:runs-on:ubuntu-lateststeps:- uses:actions/checkout@v4- name:Push to NuGetrun:nuget push MyPackage.nupkgdotnet-nuget-push:runs-on:ubuntu-lateststeps:- uses:actions/checkout@v4- name:Push with dotnetrun:dotnet nuget push MyPackage.nupkguv-publish-no-token:runs-on:ubuntu-lateststeps:- uses:actions/checkout@v4- name:Publish with uvrun:uv publishpython-m-twine:runs-on:ubuntu-lateststeps:- uses:actions/checkout@v4- name:Publish with python -m twinerun:python -m twine upload dist/*
1
2
rulesets:- CICD / GitHub # Rules to enforce / GitHub.
Request a personalized demo
Get Started with Datadog
Ask AI
AI-generated responses may be inaccurate. Verify important info.