Three Gates

chapter 04

Troubleshooting

Every problem below really happened in this implementation β€” none of these are hypothetical. Click any symptom to see the cause and the fix.

Gitleaks

A test AWS key doesn't trigger any alert at all

cause

The string used was AKIAIOSFODNN7EXAMPLE β€” the official example access key used across thousands of AWS documentation pages. Precisely because of that, it's on Gitleaks' own internal allowlist: without that exception, any repository containing a snippet of AWS documentation would generate a constant false positive.

fix

Use any other string in the same format (AKIA + 16 characters), as long as it isn't that specific value. Nothing else in the config needs to change β€” Gitleaks' allowlist is about that exact value, not the whole pattern.

A test key typed into the terminal ends up in the file as bullets (β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’)

cause

Something in the terminal environment masked text matching the AWS Access Key pattern (AKIA + 16 characters) at the moment it was typed or pasted β€” before it ever became real file content. A plain echo would never mask anything on its own; the interception happened at a layer above the shell.

fix

Build the string from two separate shell variables and concatenate them only when writing the file, instead of typing/pasting the full value at once:

p1="AKIA5G3J"
p2="XNQPZ7W2M4KD"
printf 'aws_secret_access_key = "%s%s"\n' "$p1" "$p2" > secret-test.txt

Since no single command contains the full pattern, the masking mechanism has nothing to intercept.

YAML / GitHub Actions

A new job added to the workflow simply doesn't show up

cause

Wrong indentation β€” the new job (sonarcloud:) was written at the same level as the previous job's properties (name:, runs-on:, steps:), instead of at the same level as the gitleaks: key itself. In YAML, a child key needs more indentation than its parent β€” with the wrong indentation, the parser reads everything as belonging to a single job, with duplicate keys (name, runs-on, steps each appearing twice).

fix

Validate the structure before committing, instead of trusting a visual read:

python3 -c "import yaml; d=yaml.safe_load(open('file.yml')); print(list(d['jobs'].keys()))"

If the list is missing an expected job, it's indentation. Rewriting the whole file via heredoc (instead of editing specific lines) avoids the error of manually counting spaces.

A cat > file << 'EOF' command ends up inside the file itself

cause

The block was pasted somewhere that didn't interpret the heredoc as a shell command β€” for example, inside an already-open text editor (nano/vim) instead of the bash prompt. The result is a file whose literal content starts with cat > file << 'EOF' and ends with EOF, instead of containing just the heredoc's body.

fix

Run the full command directly at the terminal prompt (not inside an editor that's already open), and validate the result before committing. For a config file like TOML, a real parser is more reliable than reading it visually:

python3 -c "import tomllib; tomllib.load(open('.gitleaks.toml', 'rb')); print('valid TOML')"

OWASP ZAP

A rule set to FAIL still shows up as WARN-NEW in the report β€” but the job fails anyway

apparent cause

The initial assumption was that the 10038 FAIL rule in zap-baseline-rules.conf was escalating that specific finding and bringing the job down because of it.

real cause

The action's own log reveals the actual reason: "By default ZAP Docker container will fail if it identifies any alerts during the scan!" β€” with fail_action: true, the job fails if any alert is found, at any severity, regardless of whether it's escalated to FAIL in the rules file. Rule 10038 stayed WARN-NEW in the report the entire time; the rules file, as written, had no effect on the outcome at all.

fix

No configuration change was needed β€” just the correct understanding: for this scenario, fail_action: true alone already guarantees the block. The rules file is still useful for deciding what to ignore (IGNORE), not for deciding what causes failure.

gh run view --log returns "logs will be available when it is complete"

cause

Not an error β€” the run is still executing. A ZAP scan takes noticeably longer than the Gitleaks/SonarCloud gates (1 to 3 minutes), so it's easy to ask for the log too soon.

fix

Wait for the run to finish before requesting the log:

gh run watch $RUN_ID

Pipeline design

The DAST workflow leaves the repository permanently "red" in Actions

cause

The workflow triggered on every push to master, but the target (Juice Shop) always has the same real findings β€” so every commit, even one completely unrelated to the pipeline, ended up failing. The gate was working correctly; the visual signal was just misleading, suggesting "broken pipeline" to anyone looking from the outside.

fix

Switch the trigger from push: branches: [master] to workflow_dispatch (manual trigger). More on why this better reflects how DAST works in practice is in chapter 05.