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-unredacted-secrets.md.
A documentation index is available at /llms.txt.
Passing GitHub Actions secrets through transformation functions such as fromJSON() is unsafe. The transformation produces a different string that GitHub’s automatic redaction can’t recognize, which can allow secret values to appear in plaintext in workflow logs.
This rule flags workflow expressions that call fromJSON with the secrets context or any child of it as an argument, for example fromJSON(secrets), fromJSON(secrets.MY_SECRET), or nested uses like fromJSON(secrets.MY_SECRET).field. Avoid storing multiple values as a single JSON secret. Instead, store individual secrets and reference them directly, or ensure any transformed value is never written to logs or exposed to third-party actions.
Secure example — reference a single secret value instead of parsing a JSON blob:
name:Safe Secret Usageon:pushjobs:# Case 1: Direct secret usage (safe - GitHub can redact this)test_direct_secret:runs-on:ubuntu-lateststeps:- name:Use secret directlyrun:| echo "Using secret safely"
curl -H "Authorization: Bearer ${{ secrets.API_TOKEN }}" https://api.example.com# Case 2: fromJSON with non-secret context (safe - not a secret)test_fromjson_env:runs-on:ubuntu-lateststeps:- name:Parse environment variableenv:CONFIG:${{ fromJSON(env.CONFIG_JSON) }}run:echo "Config parsed"# Case 3: fromJSON with literal string (safe - no secret)test_fromjson_literal:runs-on:ubuntu-lateststeps:- name:Parse literal JSONrun:echo '${{ fromJSON('{"key": "value"}') }}'# Case 4: fromJSON with inputs (safe - workflow inputs)test_fromjson_inputs:runs-on:ubuntu-lateststeps:- name:Parse workflow inputrun:echo '${{ fromJSON(inputs.config) }}'# Case 5: toJSON with secrets (different issue - not covered by this rule)test_tojson_secrets:runs-on:ubuntu-lateststeps:- name:Convert to JSONrun:echo '${{ toJSON(secrets) }}'# Case 6: Secret in env block directly (safe)test_env_direct:runs-on:ubuntu-lateststeps:- name:Environment variableenv:API_KEY:${{ secrets.API_KEY }}run:echo "Key is set"# Case 7: Multiple secrets used directly (safe)test_multiple_direct:runs-on:ubuntu-lateststeps:- name:Multiple direct secretsenv:KEY1:${{ secrets.KEY1 }}KEY2:${{ secrets.KEY2 }}run:echo "Keys configured"# Case 8: fromJSON with github context (safe - not secrets)test_fromjson_github:runs-on:ubuntu-lateststeps:- name:Parse GitHub contextrun:echo '${{ fromJSON(github.event.client_payload.data) }}'# Case 9: Secret used in conditional (safe)test_conditional_direct:runs-on:ubuntu-lateststeps:- name:Conditional on secret existenceif:${{ secrets.DEPLOY_KEY != '' }}run:echo "Deploy key exists"# Case 10: Other transformation functions (not fromJSON)test_other_functions:runs-on:ubuntu-lateststeps:- name:Use other functionsrun:| echo '${{ format('Bearer {0}', secrets.TOKEN) }}'
echo '${{ contains(secrets.ALLOWED_USERS, github.actor) }}'
Non-Compliant Code Examples
name:Unredacted Secreton:pushjobs:# Case 1: fromJSON in run commandtest_run_command:runs-on:ubuntu-lateststeps:- name:Parse secret as JSON in runrun:| CONFIG='${{ fromJSON(secrets.CONFIG_JSON) }}'
echo "Config: $CONFIG"# Case 2: fromJSON with lowercase (case insensitive)test_lowercase:runs-on:ubuntu-lateststeps:- name:Lowercase fromjsonrun:echo '${{ fromjson(secrets.DATA) }}'# Case 3: fromJSON with property accesstest_property_access:runs-on:ubuntu-lateststeps:- name:Access property of JSON secretrun:echo '${{ fromJSON(secrets.CONFIG).apiKey }}'# Case 4: fromJSON with nested property accesstest_nested_property:runs-on:ubuntu-lateststeps:- name:Access nested propertyrun:echo '${{ fromJSON(secrets.SETTINGS).database.host }}'# Case 5: fromJSON in env blocktest_env_block:runs-on:ubuntu-lateststeps:- name:Use in environment variableenv:CONFIG:${{ fromJSON(secrets.ENV_CONFIG) }}run:echo "Using config"# Case 6: fromJSON in with blocktest_with_block:runs-on:ubuntu-lateststeps:- name:Use in action parameteruses:some/action@v1with:config:${{ fromJSON(secrets.ACTION_CONFIG) }}# Case 7: fromJSON in if conditiontest_if_condition:runs-on:ubuntu-lateststeps:- name:Conditional stepif:${{ fromJSON(secrets.FEATURE_FLAGS).enabled }}run:echo "Feature enabled"# Case 8: Multiple fromJSON callstest_multiple:runs-on:ubuntu-lateststeps:- name:Multiple secret transformationsrun:| echo '${{ fromJSON(secrets.CONFIG1) }}'
echo '${{ fromJSON(secrets.CONFIG2) }}'
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.