AppScan
Product · Code monitoring

Every push. Every pull request.

Install the GitHub App once and the question stops being “is my app safe” and becomes “did this change make it worse” - which is a question you can actually answer before merging.

01Pull requests

A verdict about this change.

Findings on a branch are compared with the base branch, so what you introduced is separated from what you inherited. Nobody is asked to fix main in order to merge a feature.

  • NEW

    Raised by this change. These are the ones the check asks you to look at.

  • UNCHANGED

    Already on the base branch. Listed for context, never used to block you.

  • Resolved

    A finding this change fixed, confirmed by re-running the rule that raised it.

  • Regression

    A protection that existed before and does not exist now, raised on its own.

github.com/acme/checkout · pull/482action required
CriticalNew in this changesource.authorization.protection-removed
The admin check was removed from the refund route
- requireRole(req, 'ADMIN');
const orders = await db.order.findMany({
Highsource.tenancy.lookup-without-tenant-scope
findMany runs without an organization filter
Unchanged · 9 already on main, not raised again on this branch.
02What is read

Your code, your lockfiles, your deploy files.

Analysis runs on what you connect and keeps findings, facts and paths - not your source.

Secrets

Credentials in the diff, in the file, in the commit message and in bounded git history - masked before they are stored, and recorded as scanned-through so history is never re-read from scratch.

source.secrets.credential-committed
source.secrets.credential-in-history
Injection, by taint

Request input is tracked through the code to the sink it reaches: SQL, shell, path, HTML, template, dynamic code and query filters.

source.injection.sql-built-from-input
source.injection.path-traversal
Authorization and tenancy

Mutating routes without a guard, protections removed in a change, and lookups that forget to scope by organization.

source.authorization.unguarded-mutating-route
source.tenancy.lookup-without-tenant-scope
The money paths

Prices taken from the client, webhooks accepted without a signature, and uploads that trust whatever arrives.

source.billing.price-from-client-input
source.webhook.missing-signature-verification
Dependencies

Exact versions from your lockfiles, direct and transitive, matched against public advisories and re-evaluated daily when new ones land.

source.dependencies.known-vulnerability
Infrastructure and pipelines

Dockerfile, Compose, nginx, Kubernetes, Terraform and GitHub Actions: the files that decide how the app is really deployed.

infra.docker.runs-as-root
infra.actions.untrusted-checkout
03Invariants

The rules your code already follows.

An invariant is something true about your application that should stay true: every mutating route has a guard, every tenant lookup is scoped, no private key reaches the browser, the price comes from the server. AppScan derives them from your code, stores them, and re-checks them on every commit - so a regression is a broken fact rather than an opinion.

  • Holding

    Re-checked and still true on the current commit.

  • Broken

    Violated by a change, with the file and line that broke it.

  • Not verifiable

    The code moved somewhere the analyzer cannot follow - said plainly, not guessed.

  • Reconciled

    Re-derived after a refactor, so renaming things does not create noise.

checkout-app · invariants7 / 8 holding
  • Reads are scoped to the tenant
    Brokentenant.reads-scoped:<Model>
  • Mutating routes require authentication
    Holdingauth.mutations-guarded
  • Admin operations require admin permission
    Holdingauth.admin-routes-require-admin
  • Webhooks verify provider signatures
    Holdingwebhooks.signatures-verified
  • CORS uses an explicit origin allowlist
    Holdingcors.explicit-allowlist
  • SQL is parameterized
    Holdinginjection.sql-parameterized
  • Shell commands never include request input
    Holdinginjection.commands-not-built-from-input
  • Request input is escaped before rendering
    Holdinginjection.html-escaped
checkout-app · change sets
  • Critical
    Add bulk refund endpoint
    14 commits · 32 files
  • Low
    Bump next to 15.5.7
    1 commit · 2 files
  • High
    Move uploads to S3
    6 commits · 11 files
  • Low
    Copy changes on pricing page
    2 commits · 3 files
04Pushes

Forty commits are still one change.

Pushes are coalesced into change sets, classified by what they actually touched, and scored. A dependency bump is not read with the same seriousness as a rewritten auth middleware, and a merge storm does not produce forty scans.

  • Coalesced

    Rapid pushes to the same branch become one unit of work.

  • Classified

    Cosmetic, feature, refactor, dependency, security - scored accordingly.

  • Diffed

    Large diffs are paged, so a huge change set stays readable.

  • Cheap

    Unchanged files are not re-analyzed; inventory is incremental.

Connect a repository and watch the next push.

Install the GitHub App on one repository and see what the first change set says.