Rule-based fixes
Open fix PR on a secret finding or an IaC finding opens a pull request that fixes it. A fixed rule makes the change, not a model:
- Secrets: the hard-coded credential becomes a read of an environment variable.
- IaC: one property of a resource is set to its secure value.
The button is in the Fix column under Security › Findings › Secret scanning and Security › Findings › IaC. A person clicks it for one finding. Nothing is proposed automatically, and there is no setting to switch it on.
Nothing leaves fremforge
No AI model is involved, so no code is sent to any provider and no AI key is needed. The file is read from your repository at the commit the finding was raised on, changed in memory, re-scanned on fremforge’s own scanners and written back as a branch in your repository. The fix request record keeps ids, counts and the outcome, never the file, the diff or the credential.
Secrets: read from the environment
The credential the scanner found is replaced by a read of an environment variable, in the same expression:
| Language | Becomes |
|---|---|
| Go | os.Getenv("NAME"), with "os" added to the imports if needed; a one-line const becomes a var |
| Python | os.environ["NAME"], with import os added if needed |
| JavaScript, TypeScript | process.env.NAME |
NAME is taken from the variable or key the value is assigned to, in upper snake case (apiToken becomes API_TOKEN), or else from the rule. JSX and TSX files, and every other file type, get no fix: the cell shows a dash, with the reason for screen readers and on hover.
No fix is proposed when the credential is not inside one plain string literal on one line: a multi-line value, a template string or f-string, or a Go const block.
Moving the credential does not make it safe. It is still in the repository’s history. The pull request says so, and when a validity check found the credential active, its first line is ROTATE NOW: this credential is live. The pull request never contains the credential or any part of it. Rotate it at its source, then set the environment variable wherever the code runs.
IaC: one property, one secure value
Only checks where one property has a single secure value get a fix, in Terraform, CloudFormation and ARM templates:
| Check | Property (Terraform / CloudFormation, ARM) | Set to |
|---|---|---|
AWS-0086 S3 public access block: block public ACLs | block_public_acls / BlockPublicAcls | true |
AWS-0087 S3 public access block: block public policy | block_public_policy / BlockPublicPolicy | true |
AWS-0091 S3 public access block: ignore public ACLs | ignore_public_acls / IgnorePublicAcls | true |
AWS-0093 S3 public access block: restrict public buckets | restrict_public_buckets / RestrictPublicBuckets | true |
AZU-0008 Azure storage: HTTPS-only transfers | enable_https_traffic_only or https_traffic_only_enabled / supportsHttpsTrafficOnly | true |
AZU-0011 Azure storage: minimum TLS 1.2 | min_tls_version / minimumTlsVersion | TLS1_2 |
AZU-0059 (secure transfer) is fixed through whichever of AZU-0008 and AZU-0011 applies on its line. The property must already be written with its insecure value on the line the scanner reported. A fix never adds a property or a resource. Every other check shows a dash in the Fix column; its remediation text says what to change.
What is checked before a pull request exists
- One file, the finding’s own, and only the lines the rule changes, plus an import for a secret fix.
- Never a test, CI configuration,
CODEOWNERS, a suppression file or a dependency manifest. - Syntax check: the patched file must parse as it did before (
gofmt -efor Go, a compile for Python, a parse for JavaScript and TypeScript), so a fix cannot break the build by its syntax. - Re-scan: the same scanner and rules as the finding (gitleaks for secrets, Trivy for IaC) run on the original and the patched file. The finding must be present in the original, gone from the patched file, and no new finding may appear.
The pull request
- Opened by your organisation’s AutoFix bot,
<org>-fremforge-bot-autofix, on a new branch underautofix-rule/, with the labelautofix. It carries noai-generatedlabel, because no AI made it. - Title
fix(security): read NAME from the environment in <path>for a secret, orfix(security): <check title> in <path>for IaC. The body says what changed, how it was checked, and what to check before merging. - Never armed for auto-merge. Your required approvals and checks apply as for any pull request. A person reviews and merges it.
Limits
- One live request per finding. Ask again after 10 minutes if a request ended without a pull request.
- At most 3 open rule-based fix pull requests per repository and 20 requests per organisation in any 24 hours. These use the same settings as AI fix, with their own counter. Setting them to 0 turns rule-based fixes off.
- A finding raised by the pull request check exists only on that pull request’s branch: fix it in the pull request itself.
API
- Request a fix
POST /_app/api/v1/orgs/{org}/code-security/secrets/findings/{id}/fixorPOST /_app/api/v1/orgs/{org}/code-security/iac/findings/{id}/fix, scopefindings:write. Answers202withrequest_id,status: "locating",engine: "rule"and"automerge": false; the pull request follows within a few minutes. A refusal isproblem+jsonwith thetyperule-fix-<reason>and areason, for examplerule-fix-check-not-supportedorrule-fix-already-proposed, which also carries the existing request. - List fix requests
GET /_app/api/v1/orgs/{org}/code-security/fix-requests, scopefindings:read: kind, rule, status, reason, pull request, changed lines and times. A request that ended without a pull request says why, for examplesecret_in_template,iac_shape_not_supported,rescan_patched_syntaxorrescan_finding_still_present.
Audit
Requests are in the audit log as autofix.proposed, autofix.pr_opened, autofix.rejected, autofix.merged and autofix.closed, with engine: rule.
See also
- Secret scanning and IaC scanning: the findings the button sits on.
- AI fix: fixes for a short list of SAST rules, by your organisation’s own AI model.
- Security fixes: fixes for vulnerable dependencies and base images.