Product · LGTM Security

16 detectors.
Three enforcement gates.
Zero LLM cost.

LGTM Security scans GitHub Actions workflows, Dockerfiles, and IaC configs for the supply-chain and DevOps risks that cause real incidents. Pure code, no LLM tokens — every detector is deterministic, fast, and reproducible. Block merges. Block pushes. Auto-revert what slipped through.

Free: read-only access · Pro ₹399/mo: enroll + scan + alerts

Three enforcement gates

One detector engine, three places it runs. Inline at PR review time so devs see findings while reviewing. Merge- block at the GitHub Check Runs API so branch protection can refuse the merge. Runtime watchdog at push time on default branch so anything that slipped through still gets caught.

Gate 1

Inline PR review

Findings show up in the PR thread

When a PR includes changes to a watched file, LGTM Security runs the detectors and posts findings as inline comments anchored to the offending lines. Same UX as our regular AI review, just sourced from deterministic rules instead of LLM agents.

Trigger: pull_request opened / synchronize / reopened. Latency: ~3 seconds.

Gate 2

Merge-block Check Run

GitHub branch protection refuses the merge

LGTM Security writes a check_run with status: failure when any rule set to 'block' mode triggers. Branch protection rules respect the check the same way they respect any CI gate — no merge until resolved or rule is downgraded.

Per-rule mode: off / warn / block. Default mode varies by rule severity.

Gate 3

Runtime watchdog

Pushes that bypassed PR review get caught

A GitHub Action you install on default branch. Runs on every push, detects pushes that skipped PR review (force pushes, direct commits by privileged users), and either alerts or auto-creates a revert PR. Your choice per-repo.

Action: tarinagarwal/lgtm-security-watchdog@v1. Token-authed (mint from dashboard).

All 16 detectors, documented

Every rule lives by its ID — the same ID you see in the audit log, the same ID you override in the policy editor. Below: what each catches, why it's flagged, and a sample of what triggers it.

GitHub Actions

7 detectors
critical

Self-hosted runner on a public repo

workflow.self-hosted-runner-on-public-repo

Why this is flagged

A self-hosted runner on a public repository lets any forked PR run arbitrary code on your infrastructure — well-known fork-pwn vector. Several real-world incidents have exfiltrated AWS creds via this route.

What triggers it

`runs-on: self-hosted` on a workflow triggered by `pull_request` (no `pull_request_target` workaround needed for the attacker).

critical

Untrusted PR input flowing into `run:` blocks

workflow.untrusted-input-in-run

Why this is flagged

PR titles, branch names, issue comments and similar attacker-controlled strings interpolated into shell run-blocks become arbitrary command execution. The classic `${{ github.event.pull_request.title }}` injection.

What triggers it

`run: echo "PR: ${{ github.event.pull_request.title }}"` — title becomes shell.

high

Action pinned to a mutable ref (branch/tag) instead of SHA

workflow.action-pinned-to-mutable-ref

Why this is flagged

An action pinned to a tag or branch can be quietly retagged or force-pushed by its maintainer (intentionally or after compromise) to a malicious version. Pin to a commit SHA so the bytes can't change underneath you.

What triggers it

`uses: actions/checkout@v3` → flag. `uses: actions/checkout@8e5e7e5...` → ok.

medium

Deprecated workflow commands (`set-output`, `save-state`)

workflow.deprecated-commands

Why this is flagged

GitHub deprecated these because they were exploitable for log injection. Workflows that still use them will eventually break — and miss the security fix that came with the migration.

What triggers it

`echo "::set-output name=foo::$BAR"` instead of `echo "foo=$BAR" >> $GITHUB_OUTPUT`.

high

Missing top-level `permissions:` block

workflow.missing-permissions-block

Why this is flagged

Without an explicit `permissions:` block, workflows inherit the repo-wide default — historically `write-all`. One compromised step can mint releases, push tags, or read every secret. Set `permissions: read-all` at the top and elevate per-job only when needed.

What triggers it

No `permissions:` at all → default-permissive token reaches every step.

critical

`pull_request_target` checking out PR-controlled code

workflow.pull-request-target-checkout-pr-code

Why this is flagged

`pull_request_target` runs with the BASE repository's secrets, so checking out the head SHA hands fork code the keys to the kingdom. The pwnrequest pattern that hit several major repos.

What triggers it

`on: pull_request_target` + `uses: actions/checkout@v4 with: ref: ${{ github.event.pull_request.head.sha }}`.

high

Secret exfiltration via env-echo

workflow.secret-exfil-via-echo

Why this is flagged

`echo` or printf of a secret value into a logged step bypasses GitHub's secret masking when the secret is base64-encoded or transformed first. Pattern flagged because it's almost never legitimate.

What triggers it

`run: echo "${{ secrets.AWS_KEY }}" | base64` — base64 evades the masking pass.

Docker

5 detectors
high

`:latest` tag in `FROM`

docker.latest-tag-in-from

Why this is flagged

`:latest` is mutable — today's image is not tomorrow's image. Reproducible builds need pinned digests. The Lockfile-integrity philosophy applies to base images too.

What triggers it

`FROM node:latest` → flag. `FROM node:20-slim@sha256:...` → ok.

medium

`apt-get install` without `--no-install-recommends`

docker.apt-get-without-no-install-recommends

Why this is flagged

Without `--no-install-recommends`, apt pulls in dozens of suggested packages that grow your image, expand the attack surface, and slow rebuilds. Image bloat is a security smell — every extra package is a potential CVE.

What triggers it

`RUN apt-get install -y curl` → flag. Add `--no-install-recommends`.

low

`ADD` used where `COPY` would do

docker.add-instead-of-copy

Why this is flagged

`ADD` auto-extracts tarballs and fetches remote URLs — both have caused real incidents (tar bombs, fetched-from-mutated-URL). Use `COPY` unless you specifically need extraction; document why if you do.

What triggers it

`ADD ./local-file /app/` → use `COPY`.

high

`curl | sh` piping

docker.curl-pipe-sh

Why this is flagged

Piping a remote script directly into a shell turns your build into whatever the maintainer's web server returns this second. Fetch first, verify, then execute.

What triggers it

`RUN curl -fsSL https://example.com/install.sh | sh` — no validation, no pinning.

medium

No `USER` directive (running as root)

docker.running-as-root

Why this is flagged

Containers running as root have full filesystem write access if escape-from-container ever happens. Set a `USER` to drop privileges; `node:slim`, `python:slim`, and most language images already ship with a non-root user available.

What triggers it

Missing `USER node` or `USER 1000` → root by default.

IaC (Terraform · Kubernetes)

4 detectors
critical

S3 bucket allowing public read

iac.s3-bucket-public

Why this is flagged

Public S3 has been the root cause of repeated mass-PII leaks. The default should be private; opt into public via a known intentional path (CloudFront OAI, signed URLs, etc.).

What triggers it

`acl = "public-read"` or `block_public_acls = false` without justification.

critical

Security group `0.0.0.0/0` on sensitive ports

iac.security-group-open-to-world

Why this is flagged

Ports 22 (SSH), 3389 (RDP), 3306 (MySQL), 5432 (Postgres), 6379 (Redis), 27017 (Mongo) on 0.0.0.0/0 are how databases end up scraped by Shodan within an hour of being exposed.

What triggers it

`cidr_blocks = ["0.0.0.0/0"]` + `from_port = 22` → flag.

medium

Kubernetes pod without resource limits

iac.k8s-pod-no-resource-limits

Why this is flagged

A pod without CPU/memory limits can starve neighbors on the node. Also a noisy-neighbor security pattern — a compromised container without limits can mine crypto undetected for longer.

What triggers it

`resources: {}` or `resources` block omitted entirely.

critical

Hardcoded credentials in IaC

iac.hardcoded-credentials

Why this is flagged

Secrets in Terraform state, K8s manifests, or environment blocks end up in version control. Even after rotation, the historical commit still has them — git history is forever.

What triggers it

`AWS_SECRET_ACCESS_KEY = "AKIA..."` checked into the repo.

Per-rule policy

Tune each rule per repo

Sensible defaults out of the box — but every rule is overridable per repo. A monorepo with a known self-hosted runner intentionally serving private PRs downgrades the relevant detector to warn without touching the others.

  • block

    Finding fails the GitHub check run. Branch protection refuses merge until resolved.

  • warn

    Finding posts a non-blocking PR comment. Visible to reviewers but doesn't gate merge.

  • off

    Finding still gets recorded in the audit log (for compliance) but no PR action.

Policy changes are versioned. Every override writes an audit entry — you can see who changed what and when.

Example policy table

workflow.self-hosted-runner-on-public-repoblock
workflow.action-pinned-to-mutable-refwarn
docker.latest-tag-in-fromwarn
iac.s3-bucket-publicblock
docker.add-instead-of-copyoff

Runtime watchdog

PR review is the first gate. Merge-block is the second. The watchdog is the third — it runs on every push to your default branch and catches the things that bypassed both: force pushes by maintainers, direct commits by privileged users, repository admins pushing without opening a PR.

Setup

Add the action to your default-branch workflow + mint a token from Dashboard → Security → API Tokens.

name: LGTM Security Watchdog
on:
  push:
    branches: [main]

jobs:
  watchdog:
    runs-on: ubuntu-latest
    steps:
      - uses: tarinagarwal/lgtm-security-watchdog@v1
        with:
          token: ${{ secrets.LGTM_WATCHDOG }}

What it does

  • ·Detects pushes that didn't go through a PR (force push, direct commit, admin bypass)
  • ·Runs the 16 detectors against the push diff
  • ·On finding: either Slack/email alert OR auto-creates a revert PR
  • ·Token-scoped: write-only to LGTM, no GitHub-write access via this token

The watchdog Action is MIT-licensed and lives on GitHub. You can read the code and verify there's nothing weird before installing.

Audit log

Immutable record of every finding

Every scan produces SecurityAuditLog entries — finding + file + line + ruleId + scanId + timestamp. Past findings can be marked resolved, false_positive, or won't fix with a note — but the original entry is never deleted.

The audit log survives repo disconnects, account deletions (anonymized but retained), and rule policy changes. If a regulator asks "was this CVE flagged in our pipeline?" — the answer is in the log, provable.

  • Per-finding resolution status + free-text note
  • Filter by rule ID, severity, PR number, or free-text
  • False-positive rate computed per rule (for tuning policy)
  • Export available via API for SIEM ingestion

Example audit entries

workflow.self-hosted-runner-on-public-repo

.github/workflows/build.yml · L12

resolved6h ago
docker.latest-tag-in-from

apps/api/Dockerfile · L3

false_positive2d ago
iac.security-group-open-to-world

infra/sg-db.tf · L18

openjust now
workflow.action-pinned-to-mutable-ref

.github/workflows/deploy.yml · L24

wont_fix1w ago

Why these rules don't use LLMs

CI/CD security findings need to be reproducible, auditable, and cheap. Three things LLM scanners struggle with.

Deterministic

Same input, same output. Run the scan twice on the same diff and get the same findings. Tribunals like that. Auditors like that.

Zero LLM cost

Pure code paths — no token spend per scan. Scanning the same repo 1,000 times costs the same as scanning it once. Free tier ships with full scanner power.

No false alarms from hallucination

Rules trigger on AST-level pattern matches, not LLM judgement. False positives come from genuinely ambiguous cases, not the model getting creative.

Compliance angles

LGTM Security maps to several public security frameworks. Useful when your compliance team asks "what does this tool give us toward our SOC 2 / ISO / NIST checklist?"

OWASP CI/CD Top 10

  • ·CICD-SEC-04: Poisoned Pipeline Execution → workflow.untrusted-input-in-run
  • ·CICD-SEC-06: Insufficient Credential Hygiene → iac.hardcoded-credentials
  • ·CICD-SEC-07: Insecure System Configuration → workflow.missing-permissions-block
  • ·CICD-SEC-03: Dependency Chain Abuse → workflow.action-pinned-to-mutable-ref

NIST SSDF

  • ·PO.5.1 → audit log retention
  • ·PS.1.1 → secret detection in IaC
  • ·PW.5.1 → policy enforcement at merge time
  • ·RV.1.2 → finding triage workflow

OSSF Scorecard

  • ·Pinned-Dependencies → workflow.action-pinned-to-mutable-ref
  • ·Token-Permissions → workflow.missing-permissions-block
  • ·Dangerous-Workflow → workflow.pull-request-target-checkout-pr-code
  • ·Webhook-Validation (server) → covered by our own webhook idempotency

India DPDP Act

  • ·§24: Reasonable security practices → enforced via audit log
  • ·Breach reporting → audit + alert path
  • ·Grievance officer access → contact via /security
  • ·Data localisation note: scans run in Singapore region; no PII in findings
Free vs Pro

Free users keep their data, lose write access

When a Pro user downgrades to Free, LGTM Security flips to read-only mode. Past findings, audit log, monitor list, and scan history all stay visible. Enrolling new repos, running manual scans, editing policy, or minting watchdog tokens require Pro.

Existing monitors auto-pause on downgrade — they don't get deleted. Re-upgrade and they resume automatically. No re-enrollment, no re-token-mint, no lost configuration.

View findings
View audit log
Enroll new repo
·
Run manual scan
·
Edit policy
·
Mint watchdog token
·
Slack/email alerts
·
FreePro

FAQ

Does LGTM Security replace Snyk / Dependabot / GitHub's CodeQL?

No — it complements them. Snyk and Dependabot focus on dependency vulnerabilities (CVE databases). CodeQL is broad application security analysis. LGTM Security is narrowly focused on CI/CD risks — the supply-chain layer between your code and your runtime. Run all of them; they don't overlap.

Why deterministic rules instead of an LLM?

CI/CD findings need to be reproducible (regulator can re-run and get the same answer), auditable (you can read the rule code), and cheap (you scan on every PR + push). LLM scanners are non-deterministic, opaque, and burn tokens. We use LLMs for code review (where judgement helps) and rules here (where it doesn't).

Does it generate false positives?

Yes — like every static analyzer. The per-rule policy editor exists for this. Mark a finding as false_positive with a note and it stops blocking; the original entry stays in the audit log. Per-rule false-positive rates are computed automatically so you can see which rules need tuning for your repo.

What if I need a custom detector?

Custom rules are on the roadmap. For now, the per-rule policy editor lets you turn off rules that don't apply to your stack. If you have a specific detector you'd pay for, email tarinagarwal@gmail.com — solo-founder product, fast roadmap.

What does the GitHub App need access to for Security to work?

Read access to repo contents (to scan workflows, Docker, and IaC files). Write access to pull request comments + check runs (so findings post and Check Runs block). Webhook subscription to pull_request and push events. The watchdog Action additionally needs a token you mint from the dashboard; that token has zero GitHub permissions — it only POSTs findings to LGTM.

Can I run Security on a private/internal repo?

Yes — same flow as a public repo. Install the GitHub App, enroll the repo from the Dashboard, scans run on every applicable change. Findings stay private to your account. We never share findings across accounts.

How quickly does a scan complete?

Sub-second on the small detectors. ~3 seconds end-to-end including webhook receive, diff fetch, all 16 rules, and check-run write back to GitHub. The runtime watchdog adds another ~5 seconds because it's running inside a GitHub Action job.

Lock down your CI/CD

16 detectors. Zero LLM cost. Three enforcement gates. ₹399/mo Pro · Free read-only access to all findings.