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-unsound-contains-with-controllable-input.md.
A documentation index is available at /llms.txt.
Using contains() with a string literal to validate a user controllable value is unsafe because contains() performs substring matching. Crafted values such as refs/heads/mai or ain can satisfy the check and bypass intended restrictions.
This is especially dangerous when the value being tested comes from user-controllable contexts such as env.*, github.ref, github.ref_name, github.head_ref, github.base_ref, github.actor, github.sha, github.triggering_actor, or inputs.*. An attacker can trigger unintended workflow paths, disclosures, or privileged actions.
The rule inspects job if expressions and flags calls of the form contains(<string literal>, <Context>). Replace substring checks with explicit equality comparisons such as github.ref == 'refs/heads/main' || github.ref == 'refs/heads/develop', or use a JSON array with fromJSON and contains.
# Explicit equality checksif:github.ref == 'refs/heads/main' || github.ref == 'refs/heads/develop'# Or use a JSON array for exact membership testingif:contains(fromJSON('["refs/heads/main","refs/heads/develop"]'), github.ref)
Compliant Code Examples
name:Sound Contains Usageon:pull_requestjobs:# Case 1: Explicit equality checks (best practice)test_explicit_equality:runs-on:ubuntu-latestif:${{ github.ref == 'refs/heads/main' || github.ref == 'refs/heads/develop' }}steps:- uses:actions/checkout@v4- run:npm test# Case 2: JSON array with contains() (safe approach)test_json_array:runs-on:ubuntu-latestif:${{ contains(fromJSON('["refs/heads/main", "refs/heads/develop"]'), github.ref) }}steps:- uses:actions/checkout@v4- run:npm test# Case 3: Step-level with explicit checkstest_step_explicit:runs-on:ubuntu-lateststeps:- name:Check actorif:${{ github.actor == 'dependabot' || github.actor == 'renovate' }}run:echo "Bot detected"# Case 4: Using contains() for single substring (no spaces)test_single_substring:runs-on:ubuntu-lateststeps:- name:Check if contains featureif:${{ contains(github.ref, 'feature') }}run:echo "Feature branch"# Case 5: Using startsWith() for prefix matchingtest_starts_with:runs-on:ubuntu-latestif:${{ startsWith(github.ref, 'refs/heads/feature/') }}steps:- run:echo "Feature branch"# Case 6: Using endsWith() for suffix matchingtest_ends_with:runs-on:ubuntu-latestif:${{ endsWith(github.ref, '/main') }}steps:- run:echo "Main branch"
Non-Compliant Code Examples
name:Unsound Contains Usageon:pull_requestjobs:# Case 1: Job-level condition with space-separated branchestest_branches:runs-on:ubuntu-latestif:${{ contains('refs/heads/main refs/heads/develop', github.ref) }}steps:- uses:actions/checkout@v4- run:npm test# Case 2: Job-level condition with env variabletest_env:runs-on:ubuntu-latestif:${{ contains('production staging', env.ENVIRONMENT) }}steps:- run:echo "Deploying"# Case 3: Step-level condition with github.actortest_step_actor:runs-on:ubuntu-lateststeps:- name:Check actorif:${{ contains('dependabot renovate', github.actor) }}run:echo "Bot detected"# Case 4: Step-level with github.head_reftest_step_ref:runs-on:ubuntu-lateststeps:- name:Check branchif:${{ contains('feature/ bugfix/', github.head_ref) }}run:echo "Feature or bugfix"# Case 5: Using inputs (workflow_dispatch)test_inputs:runs-on:ubuntu-lateststeps:- name:Check inputif:${{ contains('dev test prod', inputs.environment) }}run:echo "Valid environment"# Case 6: Single quotes with pipe delimitertest_pipe_delimiter:runs-on:ubuntu-latestif:${{ contains('main|develop|staging', github.ref_name) }}steps:- run:echo "Branch check"
name:Composite action with unsafe containsdescription:Composite step if condition uses contains() against user-controllable inputruns:using:compositesteps:- name:Check inputif:${{ contains('dev test prod', inputs.environment) }}shell:bashrun:echo "Valid environment"
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.