Skip to main content

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:

LanguageBecomes
Goos.Getenv("NAME"), with "os" added to the imports if needed; a one-line const becomes a var
Pythonos.environ["NAME"], with import os added if needed
JavaScript, TypeScriptprocess.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:

CheckProperty (Terraform / CloudFormation, ARM)Set to
AWS-0086 S3 public access block: block public ACLsblock_public_acls / BlockPublicAclstrue
AWS-0087 S3 public access block: block public policyblock_public_policy / BlockPublicPolicytrue
AWS-0091 S3 public access block: ignore public ACLsignore_public_acls / IgnorePublicAclstrue
AWS-0093 S3 public access block: restrict public bucketsrestrict_public_buckets / RestrictPublicBucketstrue
AZU-0008 Azure storage: HTTPS-only transfersenable_https_traffic_only or https_traffic_only_enabled / supportsHttpsTrafficOnlytrue
AZU-0011 Azure storage: minimum TLS 1.2min_tls_version / minimumTlsVersionTLS1_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 -e for 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 under autofix-rule/, with the label autofix. It carries no ai-generated label, because no AI made it.
  • Title fix(security): read NAME from the environment in <path> for a secret, or fix(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}/fix or POST /_app/api/v1/orgs/{org}/code-security/iac/findings/{id}/fix, scope findings:write. Answers 202 with request_id, status: "locating", engine: "rule" and "automerge": false; the pull request follows within a few minutes. A refusal is problem+json with the type rule-fix-<reason> and a reason, for example rule-fix-check-not-supported or rule-fix-already-proposed, which also carries the existing request.
  • List fix requests GET /_app/api/v1/orgs/{org}/code-security/fix-requests, scope findings:read: kind, rule, status, reason, pull request, changed lines and times. A request that ended without a pull request says why, for example secret_in_template, iac_shape_not_supported, rescan_patched_syntax or rescan_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