Fix all guardrail findings (make lint, make test, make sast) across one or more repositories. This is a complete, repeatable workflow: sync with main, run all guardrails, fix every finding, update documentation, commit following standards, push, and create PRs.
For guardrail tools, refer to the CI/CD rule and Security rule. For commit conventions, refer to the Git Flow rule. For changelog updates, refer to the Documentation rule.
Vendor Detection
Auto-detect the Git hosting vendor from git remote get-url origin to determine the correct PR creation CLI:
| Remote URL contains |
Vendor |
PR CLI |
github.com |
GitHub |
gh pr create |
dev.azure.com or ssh.dev.azure.com |
Azure DevOps |
az repos pr create |
gitlab.com or gitlab |
GitLab |
glab mr create |
| Other |
Unknown |
Skip PR, report branch for manual creation |
Step-by-step Workflow (repeat for every repository)
Step 1 -- Sync with the default branch
cd <REPO_PATH>
git checkout <default-branch>
git fetch --all
git pull --rebase
- Detect the default branch with
git symbolic-ref refs/remotes/origin/HEAD (strip refs/remotes/origin/). Fall back to main if unavailable.
- If
pull --rebase fails due to conflicts, report the failure and skip this repo.
Step 2 -- Run all three guardrails
Run them in this exact order. Each gate must be evaluated independently -- even if one passes, the others may fail.
Gate 1: Lint
make lint
- Timeout: 3 minutes.
- Parse the output. Record every finding with file path, line number, rule, and message.
- NEVER invoke linter binaries directly. Always use
make lint.
Gate 2: Test
make test
- Timeout: 5 minutes.
- Record any failing test names and error messages.
- NEVER invoke test runners directly. Always use
make test.
Gate 3: SAST
make sast
- Timeout: 10 minutes -- SAST is the slowest gate (it runs CodeQL, Semgrep, Trivy, Hadolint, and Gitleaks).
- Parse the output. Record every finding with tool name, rule ID, file path, line number, and description.
- NEVER invoke SAST tools directly. Always use
make sast.
Decision point
- If all three gates pass with 0 findings: print
<repo>: SKIP(clean) and move to the next repo.
- If any gate has findings: proceed to Step 3.
Step 3 -- Create the feature branch
git checkout -b fix/<scope>
Branch naming rules (from the Git Flow rule):
- Format:
type/scope
- Choose the scope based on what was found:
- Lint-only findings:
fix/lint-findings
- SAST-only findings:
fix/sast-findings
- Test-only findings:
fix/test-findings
- Mixed findings:
fix/guardrail-findings
- If a specific tool dominates:
fix/codeql-findings, fix/semgrep-findings, etc.
- The branch type is always
fix for guardrail remediation.
Step 4 -- Fix each finding
Read the affected files, understand the context, and apply a minimal, targeted fix for each finding. Never refactor surrounding code.
4a. Lint fix patterns
| Linter / Rule |
Root Cause |
Fix |
unused variable/import |
Dead code left after refactoring |
Remove the unused variable or import |
ineffectual assignment |
Variable assigned but never read |
Remove assignment or use the value |
errcheck / unchecked error |
Return value of error-returning function ignored |
Assign to err and handle it, or explicitly ignore with _ = |
staticcheck / deprecated API |
Using deprecated function |
Replace with the recommended alternative |
govet / struct field alignment |
Struct fields not optimally ordered |
Reorder fields by size (largest first) |
revive / exported without comment |
Exported symbol missing doc comment |
Add a doc comment starting with the symbol name |
4b. Test fix patterns
| Failure Type |
Root Cause |
Fix |
| Assertion mismatch |
Code behavior changed but test not updated |
Update the test expectation to match the new correct behavior, or fix the code if the test was correct |
| Compilation error in test |
Test references a removed/renamed symbol |
Update the test to use the new symbol |
| Timeout |
Test relies on external service or has infinite loop |
Fix the logic or add proper mocking |
| Race condition |
Shared state across parallel tests |
Add proper synchronization or use t.Parallel() correctly |
Important: Tests must follow BDD structure (// given, // when, // then). If you modify a test, preserve or add this structure.
4c. SAST fix patterns
CodeQL:
| Rule |
Root Cause |
Fix |
go/useless-assignment-to-field |
Value receiver stores a parameter in a struct field used only through method values on the same local copy |
Remove the struct field. Replace callback methods with inline closures capturing the parameter. |
go/disabled-certificate-check |
InsecureSkipVerify: true hardcoded in tls.Config |
Replace the hardcoded true with a configurable setting field. Add // #nosec G402 -- controlled via configuration on the same line. |
go/sql-injection |
String concatenation in SQL queries |
Use parameterized queries ($1, $2) or whitelist-validated identifiers |
go/path-injection |
Unsanitized user input in file paths |
Sanitize with filepath.Clean and validate against an allowed base path |
go/log-injection |
User-controlled input directly in log messages |
Use structured logging fields instead of string interpolation |
Semgrep:
| Pattern |
Root Cause |
Fix |
| Hardcoded secret / credential |
API key, password, or token in source code |
Move to environment variable or secret manager |
| OWASP rule violation |
Insecure coding pattern (e.g., weak crypto, missing input validation) |
Apply the recommendation from the Semgrep rule description |
| Anti-pattern |
Code smell flagged by Semgrep rules |
Refactor to the recommended pattern |
Trivy (IaC scanning):
| Finding |
Root Cause |
Fix |
| Dockerfile misconfiguration |
Running as root, unpinned base image, etc. |
Pin image tags, add USER nonroot, use multi-stage builds |
| Kubernetes misconfiguration |
Missing resource limits, privileged containers, etc. |
Add resource limits, set securityContext properly |
| Terraform misconfiguration |
Missing encryption, overly permissive IAM, etc. |
Enable encryption, restrict IAM policies |
Hadolint (Dockerfile):
| Rule |
Root Cause |
Fix |
DL3007 |
Using latest tag |
Pin to a specific version |
DL3008 |
Unpinned apt-get install |
Pin package versions |
DL3025 |
Using CMD instead of ENTRYPOINT |
Use ENTRYPOINT for the main command |
SC2086 |
Unquoted variable in RUN |
Quote the variable |
Gitleaks (secrets):
| Finding |
Root Cause |
Fix |
| Secret detected in source |
Hardcoded secret, API key, or password |
Remove from source, add to .gitignore, rotate the exposed credential immediately |
| Secret in git history |
Previously committed secret |
Remove with git filter-branch or BFG, then rotate credentials |
For any rule not listed above: analyze the code, apply a minimal fix, and document the new pattern in the commit message body.
Step 5 -- Verify build
Run the appropriate build verification for the detected language:
| Language |
Build command |
| Go |
go build ./... |
| JavaScript/TypeScript |
npm run build or yarn build |
| Python |
python -m py_compile <main-module> |
| Java |
./gradlew build or mvn compile |
If compilation fails, diagnose and fix before proceeding.
Step 6 -- Re-run the failing guardrails
After applying fixes, re-run only the guardrails that originally failed:
make lint # if lint had findings
make test # if tests were failing
make sast # if SAST had findings
If new findings appear, fix them. Repeat until all three gates pass. If you cannot resolve a finding after 2 attempts, document it and proceed -- the PR description should note unresolved items.
Step 7 -- Update CHANGELOG.md
Read the existing CHANGELOG.md. Under ## [Unreleased], add entries in the appropriate category. Follow the Documentation rule for changelog conventions:
- Use simple past tense: "fixed", "removed", "replaced"
- Start each entry with a lowercase verb
- Use Keep a Changelog categories
Step 8 -- Stage and commit
Stage only the files you changed. Follow the Git Flow rule for commit message format:
- Format:
fix(<scope>): <subject in simple past tense>
- Use bullet points for multi-file changes
Step 9 -- Push the branch
GIT_SSH_COMMAND="ssh -o BatchMode=yes -o ConnectTimeout=15" git push -u origin <branch> --force
--force is safe because this is a new branch we just created
Step 10 -- Restore the original branch
git checkout <default-branch>
Step 11 -- Create Pull Request
Use the detected vendor CLI (GitHub: gh pr create, Azure DevOps: az repos pr create, GitLab: glab mr create).
PR title rules:
- Format:
fix(<scope>): resolved <tool/gate> findings
- Under 70 characters
- Simple past tense, lowercase
Error Handling
| Situation |
Action |
git pull --rebase fails |
Print FAIL(rebase conflict), restore branch, skip repo |
make lint times out |
Print FAIL(lint timeout), skip repo |
make test times out |
Print FAIL(test timeout), skip repo |
make sast times out |
Print FAIL(sast timeout), skip repo |
| Build fails after fix |
Diagnose and fix; if unresolvable, print FAIL(build), restore branch, skip repo |
| Re-run still has findings after 2 fix attempts |
Document unresolved items in PR description, proceed with commit |
git push fails |
Print FAIL(push: <error>), restore branch, skip repo |
| PR creation fails |
Print FAIL(pr: <error>) but branch is already pushed -- report for manual PR creation |
| Unknown rule from any tool |
Read and analyze the code, apply minimal fix, document in commit message |
| Gitleaks finds a real secret |
Stop immediately, warn the user, do NOT commit the secret -- it must be rotated first |
1---2name: fix-guardrails3description: Fix all guardrail findings (make lint, make test, make sast) across repositories. Use when the user asks to fix linting errors, test failures, SAST findings, or run the full guardrail remediation workflow.4---5
6Fix all guardrail findings (`make lint`, `make test`, `make sast`) across one or more repositories. This is a complete, repeatable workflow: sync with main, run all guardrails, fix every finding, update documentation, commit following standards, push, and create PRs.
7
8For guardrail tools, refer to the CI/CD rule and Security rule. For commit conventions, refer to the Git Flow rule. For changelog updates, refer to the Documentation rule.
9
10## Vendor Detection
11
12Auto-detect the Git hosting vendor from `git remote get-url origin` to determine the correct PR creation CLI:
13
14| Remote URL contains | Vendor | PR CLI |
15|----------------------------------------|--------------|--------------------------------------------|
16| `github.com` | GitHub | `gh pr create` |
17| `dev.azure.com` or `ssh.dev.azure.com` | Azure DevOps | `az repos pr create` |
18| `gitlab.com` or `gitlab` | GitLab | `glab mr create` |
19| Other | Unknown | Skip PR, report branch for manual creation |
20
21---
22
23## Step-by-step Workflow (repeat for every repository)
24
25### Step 1 -- Sync with the default branch
26
27```bash
28cd <REPO_PATH>
29git checkout <default-branch>
30git fetch --all
31git pull --rebase
32```
33
34- Detect the default branch with `git symbolic-ref refs/remotes/origin/HEAD` (strip `refs/remotes/origin/`). Fall back to `main` if unavailable.
35- If `pull --rebase` fails due to conflicts, report the failure and **skip this repo**.
36
37### Step 2 -- Run all three guardrails
38
39Run them **in this exact order**. Each gate must be evaluated independently -- even if one passes, the others may fail.
40
41#### Gate 1: Lint
42
43```bash
44make lint
45```
46
47- Timeout: **3 minutes**.
48- Parse the output. Record every finding with **file path**, **line number**, **rule**, and **message**.
49- NEVER invoke linter binaries directly. Always use `make lint`.
50
51#### Gate 2: Test
52
53```bash
54make test
55```
56
57- Timeout: **5 minutes**.
58- Record any failing test names and error messages.
59- NEVER invoke test runners directly. Always use `make test`.
60
61#### Gate 3: SAST
62
63```bash
64make sast
65```
66
67- Timeout: **10 minutes** -- SAST is the slowest gate (it runs CodeQL, Semgrep, Trivy, Hadolint, and Gitleaks).
68- Parse the output. Record every finding with **tool name**, **rule ID**, **file path**, **line number**, and **description**.
69- NEVER invoke SAST tools directly. Always use `make sast`.
70
71#### Decision point
72
73- If **all three gates pass** with 0 findings: print `<repo>: SKIP(clean)` and move to the next repo.
74- If **any gate has findings**: proceed to Step 3.
75
76### Step 3 -- Create the feature branch
77
78```bash
79git checkout -b fix/<scope>
80```
81
82**Branch naming rules** (from the Git Flow rule):
83
84- Format: `type/scope`
85- Choose the scope based on what was found:
86 - Lint-only findings: `fix/lint-findings`
87 - SAST-only findings: `fix/sast-findings`
88 - Test-only findings: `fix/test-findings`
89 - Mixed findings: `fix/guardrail-findings`
90 - If a specific tool dominates: `fix/codeql-findings`, `fix/semgrep-findings`, etc.
91- The branch type is always `fix` for guardrail remediation.
92
93### Step 4 -- Fix each finding
94
95Read the affected files, understand the context, and apply a **minimal, targeted fix** for each finding. Never refactor surrounding code.
96
97#### 4a. Lint fix patterns
98
99| Linter / Rule | Root Cause | Fix |
100|-------------------------------------|--------------------------------------------------|----------------------------------------------------------------|
101| `unused variable/import` | Dead code left after refactoring | Remove the unused variable or import |
102| `ineffectual assignment` | Variable assigned but never read | Remove assignment or use the value |
103| `errcheck` / unchecked error | Return value of error-returning function ignored | Assign to `err` and handle it, or explicitly ignore with `_ =` |
104| `staticcheck` / deprecated API | Using deprecated function | Replace with the recommended alternative |
105| `govet` / struct field alignment | Struct fields not optimally ordered | Reorder fields by size (largest first) |
106| `revive` / exported without comment | Exported symbol missing doc comment | Add a doc comment starting with the symbol name |
107
108#### 4b. Test fix patterns
109
110| Failure Type | Root Cause | Fix |
111|---------------------------|------------------------------------------------------|--------------------------------------------------------------------------------------------------------|
112| Assertion mismatch | Code behavior changed but test not updated | Update the test expectation to match the new correct behavior, or fix the code if the test was correct |
113| Compilation error in test | Test references a removed/renamed symbol | Update the test to use the new symbol |
114| Timeout | Test relies on external service or has infinite loop | Fix the logic or add proper mocking |
115| Race condition | Shared state across parallel tests | Add proper synchronization or use `t.Parallel()` correctly |
116
117**Important:** Tests must follow BDD structure (`// given`, `// when`, `// then`). If you modify a test, preserve or add this structure.
118
119#### 4c. SAST fix patterns
120
121**CodeQL:**
122
123| Rule | Root Cause | Fix |
124|----------------------------------|------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------|
125| `go/useless-assignment-to-field` | Value receiver stores a parameter in a struct field used only through method values on the same local copy | Remove the struct field. Replace callback methods with **inline closures** capturing the parameter. |
126| `go/disabled-certificate-check` | `InsecureSkipVerify: true` hardcoded in `tls.Config` | Replace the hardcoded `true` with a configurable setting field. Add `// #nosec G402 -- controlled via configuration` on the same line. |
127| `go/sql-injection` | String concatenation in SQL queries | Use parameterized queries (`$1`, `$2`) or whitelist-validated identifiers |
128| `go/path-injection` | Unsanitized user input in file paths | Sanitize with `filepath.Clean` and validate against an allowed base path |
129| `go/log-injection` | User-controlled input directly in log messages | Use structured logging fields instead of string interpolation |
130
131**Semgrep:**
132
133| Pattern | Root Cause | Fix |
134|-------------------------------|-----------------------------------------------------------------------|------------------------------------------------------------|
135| Hardcoded secret / credential | API key, password, or token in source code | Move to environment variable or secret manager |
136| OWASP rule violation | Insecure coding pattern (e.g., weak crypto, missing input validation) | Apply the recommendation from the Semgrep rule description |
137| Anti-pattern | Code smell flagged by Semgrep rules | Refactor to the recommended pattern |
138
139**Trivy (IaC scanning):**
140
141| Finding | Root Cause | Fix |
142|-----------------------------|------------------------------------------------------|------------------------------------------------------------|
143| Dockerfile misconfiguration | Running as root, unpinned base image, etc. | Pin image tags, add `USER nonroot`, use multi-stage builds |
144| Kubernetes misconfiguration | Missing resource limits, privileged containers, etc. | Add resource limits, set `securityContext` properly |
145| Terraform misconfiguration | Missing encryption, overly permissive IAM, etc. | Enable encryption, restrict IAM policies |
146
147**Hadolint (Dockerfile):**
148
149| Rule | Root Cause | Fix |
150|----------|-------------------------------------|---------------------------------------|
151| `DL3007` | Using `latest` tag | Pin to a specific version |
152| `DL3008` | Unpinned `apt-get install` | Pin package versions |
153| `DL3025` | Using `CMD` instead of `ENTRYPOINT` | Use `ENTRYPOINT` for the main command |
154| `SC2086` | Unquoted variable in `RUN` | Quote the variable |
155
156**Gitleaks (secrets):**
157
158| Finding | Root Cause | Fix |
159|---------------------------|----------------------------------------|------------------------------------------------------------------------------------|
160| Secret detected in source | Hardcoded secret, API key, or password | Remove from source, add to `.gitignore`, rotate the exposed credential immediately |
161| Secret in git history | Previously committed secret | Remove with `git filter-branch` or BFG, then rotate credentials |
162
163For **any rule not listed above**: analyze the code, apply a minimal fix, and document the new pattern in the commit message body.
164
165### Step 5 -- Verify build
166
167Run the appropriate build verification for the detected language:
168
169| Language | Build command |
170|-----------------------|--------------------------------------|
171| Go | `go build ./...` |
172| JavaScript/TypeScript | `npm run build` or `yarn build` |
173| Python | `python -m py_compile <main-module>` |
174| Java | `./gradlew build` or `mvn compile` |
175
176If compilation fails, diagnose and fix before proceeding.
177
178### Step 6 -- Re-run the failing guardrails
179
180After applying fixes, re-run **only the guardrails that originally failed**:
181
182```bash
183make lint # if lint had findings
184make test # if tests were failing
185make sast # if SAST had findings
186```
187
188If new findings appear, fix them. Repeat until all three gates pass. If you cannot resolve a finding after 2 attempts, document it and proceed -- the PR description should note unresolved items.
189
190### Step 7 -- Update CHANGELOG.md
191
192Read the existing `CHANGELOG.md`. Under `## [Unreleased]`, add entries in the appropriate category. Follow the Documentation rule for changelog conventions:
193
194- Use **simple past tense**: "fixed", "removed", "replaced"
195- Start each entry with a **lowercase verb**
196- Use [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) categories
197
198### Step 8 -- Stage and commit
199
200Stage only the files you changed. Follow the Git Flow rule for commit message format:
201
202- Format: `fix(<scope>): <subject in simple past tense>`
203- Use bullet points for multi-file changes
204
205### Step 9 -- Push the branch
206
207```bash
208GIT_SSH_COMMAND="ssh -o BatchMode=yes -o ConnectTimeout=15" git push -u origin <branch> --force
209```
210
211- `--force` is safe because this is a **new branch we just created**
212
213### Step 10 -- Restore the original branch
214
215```bash
216git checkout <default-branch>
217```
218
219### Step 11 -- Create Pull Request
220
221Use the detected vendor CLI (GitHub: `gh pr create`, Azure DevOps: `az repos pr create`, GitLab: `glab mr create`).
222
223**PR title rules:**
224
225- Format: `fix(<scope>): resolved <tool/gate> findings`
226- Under 70 characters
227- Simple past tense, lowercase
228
229## Error Handling
230
231| Situation | Action |
232|------------------------------------------------|-------------------------------------------------------------------------------------------|
233| `git pull --rebase` fails | Print `FAIL(rebase conflict)`, restore branch, skip repo |
234| `make lint` times out | Print `FAIL(lint timeout)`, skip repo |
235| `make test` times out | Print `FAIL(test timeout)`, skip repo |
236| `make sast` times out | Print `FAIL(sast timeout)`, skip repo |
237| Build fails after fix | Diagnose and fix; if unresolvable, print `FAIL(build)`, restore branch, skip repo |
238| Re-run still has findings after 2 fix attempts | Document unresolved items in PR description, proceed with commit |
239| `git push` fails | Print `FAIL(push: <error>)`, restore branch, skip repo |
240| PR creation fails | Print `FAIL(pr: <error>)` but branch is already pushed -- report for manual PR creation |
241| Unknown rule from any tool | Read and analyze the code, apply minimal fix, document in commit message |
242| Gitleaks finds a real secret | **Stop immediately**, warn the user, do NOT commit the secret -- it must be rotated first |