Find the flaw in the line that ships it.
GAEvery pushed commit’s source is checked for the mistakes that turn into incidents: input reaching a shell, a query or a file path; TLS and token checks switched off; unsafe deserialisation; broken ciphers; credentials in plain app storage; cleartext traffic; WebViews opened up. It covers server, web and mobile code, runs on TillDev’s own git servers, and your code never goes to a third-party scanner.
What gets scanned, and when
Unless the workspace policy is off, every branch push, every green CI build and every release is scanned, and anyone with read access can start one by hand (30 an hour per workspace). After the first scan of a repository, a scan reads only the files changed since the last one and carries the rest. Each commit gets a tilldev/sast check, which a branch rule can require like any other.
JavaScript and TypeScript, Python, Go, Java, Kotlin, Swift and Dart are read, along with Android manifests and network security config, and iOS Info.plist. Comments are ignored, and so is text inside string literals. Tests, vendored and generated code, minified bundles and files over 1 MiB are skipped. A scan that passes a size limit is marked partial, so a clean result is never claimed for code that wasn’t read.
# Scan the default branch head and list what was found.
tilldev forge sast acme-api
# A branch or tag; wait for the result. Exits 1 when a finding blocks, 2 while not scanned.
tilldev forge sast acme-api --ref feature/login --wait
# In CI: fail on anything high or worse, whatever the workspace policy says.
tilldev forge sast acme-api --commit "$GIT_COMMIT" --fail-at high --wait
# Every active finding across the default branches you can read.
tilldev forge sast findings --severity highWarn, or block merges
| Mode | What it does |
|---|---|
| warn (default) | Every commit is scanned; the check passes and lists what was found. |
| block | The check fails, and merges into protected branches are refused, while a finding at the bar or above is active (high by default; critical or medium also possible). A merge waits while its head commit is being scanned. |
| off | Scans run only when someone asks for one. |
Owners and admins set the policy in TillForge → Code scanning, with the CLI or through the API. A change re-grades recent commits and re-posts their checks.
# Owners and admins.
tilldev forge sast policy --mode block --block-at high
tilldev forge sast accept acme-api <scan-id> <finding-id> --reason "the id is parsed as an integer first"
tilldev forge sast exceptions acme-api
tilldev forge sast withdraw acme-api <exception-id>Accepting a finding
When a finding is safe where it stands, an owner or admin accepts it with a reason from the repository’s Findings tab or with forge sast accept. The acceptance follows the rule, the file and the text of the line, so it survives edits elsewhere and lapses the moment that line changes. Withdrawing one makes the finding count again. Every decision is in the audit log.
To keep the reason next to the code, put an allow comment on the line or the line above:
// tillforge:allow-code sql-injection table name comes from a fixed list
db.query("select count(*) from " + TABLES[kind])The 15 rules
Each rule names its CWE and its OWASP Top 10 and MASVS categories, so findings line up with the frameworks your auditors use. tilldev forge sast rules prints the same list.
| Rule | Severity | Maps to | Reads |
|---|---|---|---|
Shell command built from a variablecommand-injection | critical | CWE-78 · A05 · MASVS-CODE-4 | JS/TS, Python, Go, Java, Kotlin |
Code evaluated from a variablecode-eval | high | CWE-95 · A05 · MASVS-CODE-4 | JS/TS, Python |
SQL built from a variablesql-injection | high | CWE-89 · A05 · MASVS-CODE-4 | JS/TS, Python, Go, Java, Kotlin, Dart, Swift |
TLS certificate checks turned offtls-verification-disabled | high | CWE-295 · A04 · MASVS-NETWORK-1 | JS/TS, Python, Go, Java, Kotlin, Dart, Swift |
Token signature or expiry not checkedjwt-verification-disabled | high | CWE-347 · A07 · MASVS-AUTH-1 | JS/TS, Python, Go, Java, Kotlin |
File path taken from the requestpath-traversal | high | CWE-22 · A01 | JS/TS, Python, Go |
Server fetches a URL from the requestssrf | high | CWE-918 · A01 | JS/TS, Python, Go |
Redirect target taken from the requestopen-redirect | medium | CWE-601 · A01 | JS/TS, Python, Go |
Untrusted data deserialised into objectsunsafe-deserialization | high | CWE-502 · A08 · MASVS-CODE-4 | Python, JS/TS, Java, Kotlin |
Broken or ECB-mode cipherweak-cipher | high | CWE-327 · A04 · MASVS-CRYPTO-1 | JS/TS, Java, Kotlin, Go, Python, Swift, Dart |
Predictable random value used for a secretinsecure-randomness | medium | CWE-338 · A04 · MASVS-CRYPTO-1 | JS/TS, Python, Go, Java, Kotlin, Dart, Swift |
Credential kept in plain app storagetoken-plain-storage | medium | CWE-922 · A04 · MASVS-STORAGE-1 | JS/TS, Dart, Java, Kotlin, Swift |
Cleartext HTTP allowedcleartext-traffic | medium | CWE-319 · A04 · MASVS-NETWORK-1 | Android XML, iOS plist |
WebView bridge, file access or debugging opened upwebview-unsafe | medium | CWE-749 · A02 · MASVS-PLATFORM-2 | Java, Kotlin, JS/TS, Dart, Swift, Android XML |
HTML written from a variablehtml-injection | medium | CWE-79 · A05 · MASVS-PLATFORM-2 | JS/TS, Python, Dart |
Next: Branch protection to require the tilldev/sast check, or the security model. Back to the TillForge overview.