PS Product SecurityKnowledge Base

CI/CD and Runner Security Assessment Pack

Intro: These exercises are meant to feel like engineering assessment tasks for Product Security. The snippets are short, but each one hides a trust-boundary mistake.

Task 1 - Jenkins pipeline reads secrets into shell and prints too much

pipeline {
  agent any
  stages {
    stage('Deploy') {
      steps {
        withCredentials([string(credentialsId: 'prod-api-key', variable: 'API_KEY')]) {
          sh '''
            set -x
            curl -H "Authorization: Bearer $API_KEY" https://deploy.example.com/release
          '''
        }
      }
    }
  }
}

Prompt

Find the weakness and propose a safer pattern.

Reveal the worked answer

Weakness

set -x can echo commands and potentially expose secrets in logs or downstream tooling. Even masked output handling is not something to trust blindly here.

Better pattern

  • avoid debug echo around secret-bearing commands;
  • keep logs minimal;
  • prefer narrow-scope deploy identity and reviewed wrapper scripts.
withCredentials([string(credentialsId: 'prod-api-key', variable: 'API_KEY')]) {
  sh '''
    set +x
    curl --fail -sS -H "Authorization: Bearer $API_KEY" https://deploy.example.com/release
  '''
}

Task 2 - GitHub Actions workflow grants more token access than needed

name: ci
on: [push, pull_request]
permissions: write-all
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm test

Prompt

What is wrong even if this workflow only runs tests?

Reveal the worked answer

Problem

write-all violates least privilege. If a third-party action or job step is compromised, the workflow token may modify repository state far beyond what the job requires.

Better version

permissions:
  contents: read

Task 3 - GitLab runner uses if-not-present pull policy on a shared fleet

[runners.docker]
  image = "alpine:3.20"
  pull_policy = "if-not-present"

Prompt

Why is this a problem in mixed-trust shared runners?

Reveal the worked answer

Why risky

A private image pulled by one job may remain cached locally and be reused by another job that should not have access to it.

Better pattern

  • prefer always on mixed-trust shared fleets;
  • or use never only when administrators pre-stage an approved local image set;
  • avoid shared runner trust mixing.

Task 4 - Self-hosted GitHub runner on a public repo

Scenario

A team attaches a self-hosted runner to a public repo because they need access to a private network dependency during CI.

Prompt

Explain the risk and the safer alternatives.

Reveal the worked answer

Risk

Any hostile pull request path may reach the self-hosted environment, persist compromise, steal secrets, or pivot internally.

Better alternatives

  • keep untrusted PR testing on GitHub-hosted runners;
  • move internal integration tests behind a reviewed promotion step;
  • use a separate low-trust isolated runner fleet with no secret or private-network reach;
  • use service virtualization or controlled artifact mirrors.