-
Notifications
You must be signed in to change notification settings - Fork 127
Harden CI: Artifactory OIDC, fork-aware workflows, composer.lock #253
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,56 @@ | ||
| name: "Artifactory OIDC - PHP/Composer" | ||
| description: "Exchange GitHub OIDC token for Artifactory access and configure Composer to resolve through Artifactory" | ||
|
|
||
| inputs: | ||
| artifactory_url: | ||
| description: "Artifactory base URL (e.g. https://segment.jfrog.io)" | ||
| required: true | ||
|
|
||
| runs: | ||
| using: "composite" | ||
| steps: | ||
| - name: Exchange OIDC token for Artifactory access token | ||
| id: oidc | ||
| shell: bash | ||
| env: | ||
| ARTIFACTORY_URL: ${{ inputs.artifactory_url }} | ||
| run: | | ||
| # Request OIDC token from GitHub | ||
| OIDC_TOKEN=$(curl -sS -H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \ | ||
| "$ACTIONS_ID_TOKEN_REQUEST_URL&audience=jfrog-github" | jq -r '.value') | ||
|
|
||
| if [ -z "$OIDC_TOKEN" ] || [ "$OIDC_TOKEN" = "null" ]; then | ||
| echo "::error::Failed to obtain OIDC token" | ||
| exit 1 | ||
| fi | ||
|
|
||
| # Exchange OIDC token for Artifactory access token | ||
| ART_TOKEN=$(curl -sS -X POST \ | ||
| "$ARTIFACTORY_URL/access/api/v1/oidc/token" \ | ||
| -H "Content-Type: application/json" \ | ||
| -d "{\"grant_type\": \"urn:ietf:params:oauth:grant-type:token-exchange\", \"subject_token\": \"$OIDC_TOKEN\", \"subject_token_type\": \"urn:ietf:params:oauth:token-type:id_token\", \"provider_name\": \"github\"}" \ | ||
| | jq -r '.access_token') | ||
|
|
||
| if [ -z "$ART_TOKEN" ] || [ "$ART_TOKEN" = "null" ]; then | ||
| echo "::error::Failed to exchange OIDC token for Artifactory access token" | ||
| exit 1 | ||
| fi | ||
|
|
||
| echo "::add-mask::$ART_TOKEN" | ||
| echo "art_token=$ART_TOKEN" >> "$GITHUB_OUTPUT" | ||
|
|
||
| - name: Configure Composer to use Artifactory | ||
| shell: bash | ||
| env: | ||
| ART_TOKEN: ${{ steps.oidc.outputs.art_token }} | ||
| ARTIFACTORY_URL: ${{ inputs.artifactory_url }} | ||
| run: | | ||
| HOST=$(echo "$ARTIFACTORY_URL" | sed 's|https://||') | ||
|
|
||
| # Configure Composer to use the Artifactory Packagist mirror | ||
| composer config --global repositories.packagist composer "https://:${ART_TOKEN}@${HOST}/artifactory/api/composer/virtual-php-thirdparty" | ||
|
|
||
| # Disable the default packagist.org to force all resolution through Artifactory | ||
| composer config --global repositories.packagist.org false | ||
|
|
||
| echo "Composer configured to resolve packages through Artifactory" |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,158 @@ | ||||||
| name: CI | ||||||
|
|
||||||
| on: | ||||||
| push: | ||||||
| branches: [master, main] | ||||||
| pull_request: | ||||||
| workflow_dispatch: | ||||||
|
|
||||||
| permissions: | ||||||
| id-token: write | ||||||
| contents: read | ||||||
|
|
||||||
| env: | ||||||
| ARTIFACTORY_URL: ${{ vars.ARTIFACTORY_URL }} | ||||||
|
|
||||||
| jobs: | ||||||
| coding-standard: | ||||||
| name: Coding Standards | ||||||
| runs-on: ${{ github.event.pull_request.head.repo.fork && 'ubuntu-latest' || 'ubuntu-x64' }} | ||||||
|
|
||||||
| steps: | ||||||
| - name: Checkout repository | ||||||
| uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 | ||||||
|
|
||||||
| - name: Set up PHP | ||||||
| uses: shivammathur/setup-php@cf4cade2721270509d5b1c766dad417c269cbb11 # v2 | ||||||
| with: | ||||||
| php-version: "8.2" | ||||||
| coverage: none | ||||||
| tools: cs2pr | ||||||
|
|
||||||
| - name: Configure Artifactory | ||||||
| if: ${{ !github.event.pull_request.head.repo.fork }} | ||||||
| uses: ./.github/actions/artifactory-oidc | ||||||
| with: | ||||||
| artifactory_url: ${{ vars.ARTIFACTORY_URL }} | ||||||
|
|
||||||
| - name: Install Composer dependencies | ||||||
| run: composer install --no-scripts --no-interaction --prefer-dist | ||||||
|
|
||||||
| - name: Check coding standards | ||||||
| continue-on-error: true | ||||||
| run: ./vendor/bin/phpcs -s --report-full --report-checkstyle=./phpcs-report.xml | ||||||
|
|
||||||
| - name: Show PHPCS results in PR | ||||||
| run: cs2pr ./phpcs-report.xml | ||||||
|
|
||||||
| lint: | ||||||
| name: "Lint: PHP ${{ matrix.php }}" | ||||||
| runs-on: ${{ github.event.pull_request.head.repo.fork && 'ubuntu-latest' || 'ubuntu-x64' }} | ||||||
| strategy: | ||||||
| fail-fast: false | ||||||
| matrix: | ||||||
| php: ["8.1", "8.2", "8.3", "8.4"] | ||||||
|
|
||||||
| steps: | ||||||
| - name: Checkout repository | ||||||
| uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 | ||||||
|
|
||||||
| - name: Set up PHP | ||||||
| uses: shivammathur/setup-php@cf4cade2721270509d5b1c766dad417c269cbb11 # v2 | ||||||
| with: | ||||||
| php-version: ${{ matrix.php }} | ||||||
| coverage: none | ||||||
| tools: cs2pr | ||||||
|
|
||||||
| - name: Configure Artifactory | ||||||
| if: ${{ !github.event.pull_request.head.repo.fork }} | ||||||
| uses: ./.github/actions/artifactory-oidc | ||||||
| with: | ||||||
| artifactory_url: ${{ vars.ARTIFACTORY_URL }} | ||||||
|
|
||||||
| - name: Install Composer dependencies | ||||||
| run: composer install --no-scripts --no-interaction --prefer-dist | ||||||
|
|
||||||
| - name: Lint against parse errors | ||||||
| run: composer lint | ||||||
|
|
||||||
| test: | ||||||
| name: "Test: PHP ${{ matrix.php }}" | ||||||
| needs: [coding-standard, lint] | ||||||
| runs-on: ${{ github.event.pull_request.head.repo.fork && 'ubuntu-latest' || 'ubuntu-x64' }} | ||||||
| strategy: | ||||||
| fail-fast: false | ||||||
| matrix: | ||||||
| php: ["8.1", "8.2", "8.3", "8.4"] | ||||||
|
|
||||||
| steps: | ||||||
| - name: Checkout repository | ||||||
| uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 | ||||||
|
|
||||||
| - name: Set up PHP | ||||||
| uses: shivammathur/setup-php@cf4cade2721270509d5b1c766dad417c269cbb11 # v2 | ||||||
| with: | ||||||
| php-version: ${{ matrix.php }} | ||||||
| coverage: xdebug | ||||||
| ini-values: error_reporting=E_ALL, display_errors=On | ||||||
| extensions: curl, json, mbstring | ||||||
|
|
||||||
| - name: Configure Artifactory | ||||||
| if: ${{ !github.event.pull_request.head.repo.fork }} | ||||||
| uses: ./.github/actions/artifactory-oidc | ||||||
| with: | ||||||
| artifactory_url: ${{ vars.ARTIFACTORY_URL }} | ||||||
|
|
||||||
| - name: Install Composer dependencies | ||||||
| run: composer install --no-scripts --no-interaction --prefer-dist | ||||||
|
|
||||||
| - name: Run tests | ||||||
| run: ./vendor/bin/phpunit --no-coverage | ||||||
|
|
||||||
| - name: Run tests with coverage | ||||||
| if: ${{ matrix.php == '8.2' }} | ||||||
| run: ./vendor/bin/phpunit | ||||||
|
|
||||||
| - name: Upload coverage to Codecov | ||||||
| if: ${{ success() && matrix.php == '8.2' }} | ||||||
| uses: codecov/codecov-action@v5 | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Semgrep identified an issue in your code: Mutable tag More details about thisThe Attack scenario: An attacker compromises the codecov organization and updates the This type of attack has real precedent—the trivy-action and kics-github-action were both compromised in similar supply-chain attacks that affected thousands of repositories. Other steps in your workflow like To resolve this comment: ✨ Commit fix suggestion
Suggested change
View step-by-step instructions
Alternatively, if you need a newer Codecov release, update to that release first and then pin 💬 Ignore this findingReply with Semgrep commands to ignore this finding.
Alternatively, triage in Semgrep AppSec Platform to ignore the finding created by github-actions-mutable-action-tag. Need help with this issue? Consult our appsec team or ask in #help-appsec on Slack. You can view more details about this finding in the Semgrep AppSec Platform. |
||||||
| with: | ||||||
| files: ./build/logs/clover.xml | ||||||
| fail_ci_if_error: true | ||||||
| env: | ||||||
| CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} | ||||||
|
|
||||||
| build-verification: | ||||||
| name: Build Verification | ||||||
| needs: [test] | ||||||
| runs-on: ${{ github.event.pull_request.head.repo.fork && 'ubuntu-latest' || 'ubuntu-x64' }} | ||||||
|
|
||||||
| steps: | ||||||
| - name: Checkout repository | ||||||
| uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 | ||||||
|
|
||||||
| - name: Set up PHP | ||||||
| uses: shivammathur/setup-php@cf4cade2721270509d5b1c766dad417c269cbb11 # v2 | ||||||
| with: | ||||||
| php-version: "8.2" | ||||||
| coverage: none | ||||||
|
|
||||||
| - name: Configure Artifactory | ||||||
| if: ${{ !github.event.pull_request.head.repo.fork }} | ||||||
| uses: ./.github/actions/artifactory-oidc | ||||||
| with: | ||||||
| artifactory_url: ${{ vars.ARTIFACTORY_URL }} | ||||||
|
|
||||||
| - name: Install Composer dependencies (production) | ||||||
| run: composer install --no-scripts --no-interaction --prefer-dist --no-dev --optimize-autoloader | ||||||
|
|
||||||
| - name: Verify autoload | ||||||
| run: php -r "require 'vendor/autoload.php'; echo 'Autoload OK' . PHP_EOL;" | ||||||
|
|
||||||
| - name: Verify package structure | ||||||
| run: | | ||||||
| # Ensure required files exist for Packagist | ||||||
| test -f composer.json | ||||||
| test -f LICENSE.md | ||||||
| test -d lib | ||||||
| echo "Package structure verified" | ||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,3 @@ | ||
| composer.lock | ||
| vendor | ||
| composer.phar | ||
| test/analytics.log | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Semgrep identified an issue in your code:
The codecov-action is pinned to a mutable semantic version tag instead of a full commit SHA, allowing attackers who compromise the codecov repository to inject malicious code into your workflow.
More details about this
The
codecov/codecov-action@v5action is pinned to a semantic version tag (@v5) instead of a full commit SHA. This creates a security risk because the maintainers of the codecov-action repository could push a malicious update to thev5tag, and your workflow would automatically pull and execute the compromised code without warning.Exploit scenario:
v5tag, overwriting the legitimate code with a backdoorUpload coverage to Codecovchecks out and executes the malicious version of codecov-actionCODECOV_TOKENsecret (which is already exposed to this action) or inject code into your build artifactsactions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683, GitHub cannot detect this tampering because the tag points to whatever the attacker pushedOther actions in this workflow like
actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683andshivammathur/setup-php@cf4cade2721270509d5b1c766dad417c269cbb11correctly use full commit SHAs, making them immutable against this attack.To resolve this comment:
✨ Commit fix suggestion
View step-by-step instructions
uses: codecov/codecov-action@v5with a full 40-character commit SHA from thecodecov/codecov-actionrepository, for exampleuses: codecov/codecov-action@<full-commit-sha>.# v5, so it stays readable while still being immutable.v5release or tag you intend to use, instead of a moving tag reference. This prevents the workflow from silently picking up different action code later.Alternatively, if you do not want to depend on the third-party action at all, replace the
uses:step with arun:step that uploads coverage through a trusted tool already installed in the job.💬 Ignore this finding
Reply with Semgrep commands to ignore this finding.
/fp <comment>for false positive/ar <comment>for acceptable risk/other <comment>for all other reasonsAlternatively, triage in Semgrep AppSec Platform to ignore the finding created by third-party-action-not-pinned-to-commit-sha.
Need help with this issue? Consult our appsec team or ask in #help-appsec on Slack.
You can view more details about this finding in the Semgrep AppSec Platform.