I've been using Amazon Q Developer's security scanning for a few months, and while the findings are great, I kept hitting the same wall: manually applying its suggested fixes across multiple repos was getting tedious. The "Explain" and "Fix" features are interactive, but I wanted something more automated for our team's standard issues.
So I built a custom workflow that takes Q's security findings and automatically creates a branch, applies the specific fix, and opens a PR. Here's a step-by-step breakdown of how I set it up, focusing on a common finding: "Hardcoded secrets in infrastructure code."
**Step 1: Parse Q's Security Findings Output**
First, I needed to get the findings in a machine-readable format. I configured our CI pipeline (GitHub Actions in this case) to run `q scan` and output to SARIF, which is great for structured data.
```yaml
# In our security scan job
- name: Run Amazon Q Security Scan
run: |
q scan ./infra --format sarif --output-file q-findings.sarif
```
**Step 2: Filter and Trigger on Specific Findings**
I wrote a Python script to parse the SARIF file and look for specific rule IDs (like `SecretsInCode`). When found, it extracts the file path, line number, and the secret context.
```python
# parse_findings.py (simplified)
import json
with open('q-findings.sarif') as f:
data = json.load(f)
for run in data['runs']:
for result in run['results']:
if result['ruleId'] == 'SecretsInCode':
file_path = result['locations'][0]['physicalLocation']['artifactLocation']['uri']
line_num = result['locations'][0]['physicalLocation']['region']['startLine']
# Logic to trigger fix workflow...
```
**Step 3: Automate the Fix Application**
This was the trickiest part. Instead of trying to use Q's API directly (which isn't fully exposed for this), I mapped common findings to our own remediation scripts. For the hardcoded secret, the script would:
1. Create a new branch (`fix/q-secret-`).
2. Replace the hardcoded value with a parameter reference (e.g., for Terraform, swap the value for a `var.secret_value`).
3. Commit the change with a standardized message.
**Step 4: Open a Pull Request with Context**
Finally, the workflow uses the GitHub CLI to open a PR. The description is auto-populated with the finding details from the SARIF file, linking back to the original scan report.
The outcome? It's cut down the manual fix time for our common, low-risk findings by about 80%. It's not a silver bullet—high-risk items still get manual review—but it keeps the backlog clean. Has anyone else built something similar? I'm curious how you're handling the integration, especially for more complex fixes where Q suggests multi-line changes.
SARIF parsing is a decent start, but you're building a chain that fails at the first weak link: the scan findings themselves. I've seen Q miss rotated credentials and flag false positives on placeholder patterns. Automating fixes based on that output means you'll eventually auto-commit nonsense to main because the tool was wrong.
Also, secrets in code shouldn't be "fixed" by an automated code patch. They need to be purged from history and the actual secret rotated. Your workflow creates a visible PR trail that a secret was there. That's a security incident on its own. Use pre-commit hooks and vault integration to prevent the secret being written in the first place, not a band-aid after the fact.
Don't panic, have a rollback plan.
user49 makes a fair point about the false positive risk, but I've been on both sides of this fence. In my consulting days, I watched a team auto-commit a "fix" for a flagged hardcoded API key that was actually a placeholder in a test template. The fix replaced it with an empty string, broke the test suite, and the PR sailed through because nobody looked at it. That's the nightmare scenario.
Where I think there's some middle ground: you can still automate the workflow, but gate it on human review. Parse the SARIF, create the branch, apply the fix, but instead of opening a PR automatically, open a draft PR with a label "auto-fix: needs review." That way you get the convenience of the branch and patch without the auto-merge risk. For something like hardcoded secrets, I'd go further: never auto-commit that finding. Flag it, block the PR, and route it to security ops for rotation + history purge. The automated PR trail is a real liability - you're right about that.
But I'll push back a little on the "pre-commit hooks solve everything" angle. We've all seen developers bypass pre-commit hooks with --no-verify because they're in a hurry. Or they commit secrets in a file that isn't caught by the hook's regex. Automation at the PR stage is a needed safety net, even if it's not the first line of defense. The real question is what kind of finding you're automating. For secrets? Hard no to auto-apply. For something like "unencrypted S3 bucket policy" where the fix is deterministic and low-risk? I'd let it fly with a review step.
Implementation is 80% process, 20% tool.
You're absolutely right about the draft PR with a label being the sweet spot. I've used that exact pattern for linting fixes and it works well. It gives the team a head start without the risk of things slipping through.
That said, I think you've hit on the core tension: automation speed versus security liability. For something like a missing semicolon, an auto-fix PR is fine. For a potential secret, automation should stop at notification and the creation of a ticket. The workflow should branch, pun intended.
The --no-verify bypass is a real problem, too. It's why we ended up pairing pre-commit hooks with a separate, mandatory CI scan that can't be skipped. If that scan finds a secret, it fails the build and pings the security channel. It's not perfect, but it closes the loop after the commit is made but before it's merged.
api first
Nice setup with the SARIF output! I've been down a similar path with other static analysis tools, and I found the filtering step is where things get tricky.
What's your approach for handling different severities? We tried auto-fixing low-sev formatting issues but had to add extra checks because a rule ID sometimes fires for multiple CWE codes. Ended up adding a secondary filter on `result.properties.security-severity` in the SARIF.
Also, curious - are you feeding these findings into a queue or directly triggering the branch creation? I've used Zapier to parse the SARIF and push to a Make scenario for more complex routing.
Webhooks or bust.