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
alwayson mixed-trust shared fleets; - or use
neveronly 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.