Product

Product Features SDLC How to Compare Docs Screenshots

Start here

Getting started Download Questions

More

Security Roadmap About

How to do almost anything here

136 answers to the questions people actually have while setting this up and running it. Type what you are trying to do and the list narrows as you type. It all runs in your browser, so there is nothing to wait for.

Press / anywhere on this page to jump into the box, Enter to open the first match, Esc to clear.

Showing all 136 answers.

Installing

Getting it onto a server and running for the first time.

9
1 How do I install it?

One command on any Linux machine with git and docker including the compose plugin:

curl -fsSL https://git.failoverlb.net/github-code-review/-/raw/master/get-gcr.sh | bash

It clones the project, asks six questions that all have sensible defaults, builds the image, starts it, waits for the health check and prints the sign in details.

installdockersetupget started
2 What does it need to run?

A Linux host with git and docker, four cores, eight gigabytes of memory and fifty gigabytes of disk to start. Also a gigabyte of shared memory, which matters more than it sounds: without it the scan writes the cleartext of found credentials to a disk instead of keeping it in memory.

requirementsmemorycpudiskshm
3 Can I read the install script before running it?

Yes, and you should. It is short and it is in the repository along with everything else. Download it, read it, then run it. Piping a script into a shell is a habit worth being uneasy about.

securitycurlscript
4 Is it safe to run the installer again?

Yes. It reads the .env file you already have and offers those answers back, so changing one thing does not mean retyping the rest. It will not reset your database and it will not reset your password.

upgradereinstallenv
5 How do I upgrade?

Pull the repository and run the installer again. It rebuilds the image and restarts the container, and the database migrates itself on the next boot. Check the Scan history page first: recreating the container during a scan interrupts it.

upgradeupdatemigrate
6 Do I need to expose it to the internet?

No, and you should not. It only needs to reach your git hosts and the public vulnerability databases, both outbound. People reach it on your internal network, through a reverse proxy that terminates TLS.

networkfirewallproxytls
7 How do I put it behind nginx or Apache?

Leave it listening on loopback and point a server block at it. Three things have to be right or you will lose an afternoon: forward the original host and scheme, allow a generous body size, and allow a long read timeout. If sign in loops with no error, it is the scheme.

nginxapachereverse proxytlshttps
8 Where is the first administrator password?

Printed once by the installer, and written to one file inside the database volume with mode 600. If you missed it:

docker compose exec github-code-review \
  cat /data/docker/github-code-review/db/first-admin.txt

The application deletes that file once the account has changed its password and set up a second factor.

passwordfirst loginadmin
9 How do I check it actually came up?
docker compose ps
docker compose logs --tail 50 github-code-review
curl -sf http://127.0.0.1:8080/health && echo healthy

The health endpoint answers before anybody signs in, so it is the right thing for a monitor or a load balancer to poll.

healthlogsdockermonitoring

Signing in

Passwords, authenticator apps, single sign on and getting back in.

6
10 Do I have to set up two step?

For a local account, yes, and it is not configurable. An administrator account on a system that knows where every credential in your organization has leaked is worth attacking. Single sign on accounts use whatever your identity provider enforces.

2famfatotpsecurity
11 I lost my authenticator. How do I get back in?

Use one of the recovery codes you were given at setup. Each works once, in place of the six digits. If those are gone too, another administrator can reset your second factor from Users and access.

recovery codes2falockout
12 How do I set up single sign on?

Any OpenID Connect provider. You need the issuer address, a client id and a client secret, and a redirect address pointing back at your installation. Then map groups from the provider to the three roles here, so joining a group is how somebody gets an account.

ssooidcoktaentraazure ad
13 Should I keep a local account if I use single sign on?

Yes. One local administrator with a second factor, for the day your identity provider is the thing that is broken. Both sign in methods can be on at once.

ssobreak glasslocal account
14 Sign in loops forever and shows no error. Why?

The reverse proxy is not forwarding the original scheme, so the application believes it is on plain HTTP and refuses to set a secure cookie. Forward the scheme and the host and it stops immediately.

proxycookiehttpstrouble
15 How long does a session last?

Both an absolute lifetime and an idle timeout, and both are settings. The defaults are twelve hours and sixty minutes. Sign in attempts are rate limited and an account locks for a while after too many failures.

sessiontimeoutlockout

Connections

Reaching your code, on twelve kinds of host.

15
23 What is a connection?

One place your code lives: a host, a read only credential and a scope saying which organizations inside it you care about. Most organizations need several, because an estate that grew by acquisition usually has code on three or four different hosts.

connectionsetupscope
24 Which hosts are supported?

GitHub, GitLab, Bitbucket, Gitea, Forgejo, Azure Repos, AWS CodeCommit, Google Cloud Source Repositories, Assembla, Beanstalk, Subversion and Perforce Helix Core.

githubgitlabbitbucketgiteaforgejoazureawssvnperforce
25 How do I connect Gitea or Forgejo?

A token from Settings, Applications with read:repository, read:organization and read:user. Type the address of your server and the API address fills itself in, because it is the same host with /api/v1 on the end. One token reaches everything its account can see, so a single connection usually covers the whole instance.

Forgejo is a fork of Gitea and speaks the same API, so pick the one you actually run: it is the name that appears on every finding and in every report.

giteaforgejotokenself hosted
26 What is missing on Gitea and Forgejo?

Dependency alerts, code scanning and secret scanning alerts, because neither host has them. Those passes are skipped rather than called and read as empty. Organization members, teams and pull requests exist there and are not wired up: the member listing has no role filter, so asking it for the administrators returns everybody, and a report saying the whole organization owns it is worse than one that does not say.

Everything that reads code works: credentials across the full history, code analysis, dependencies, licenses, infrastructure files and the triage.

giteaforgejolimits
27 What permissions does the GitHub token need?

A classic personal access token with repo and read:org. Add security_events if you want the host's own Dependabot, code scanning and secret scanning alerts folded in. Nothing that can write, ever.

githubtokenpatscopespermissions
28 My GitHub scan is missing whole organizations. Why?

The token is authorized for some organizations under single sign on and not others. GitHub does not return an error for this: it returns a success with the missing repositories quietly absent, and marks the response with a header. The scanner watches for that header and warns. Fix it on the token page under Configure SSO.

ssosamlgithubtruncatedmissing repositories
29 Should I use a fine grained token?

You can, with a caveat that catches everybody: a fine grained token belongs to one resource owner and can only cover repositories under that owner. On an estate with twenty six organizations that means twenty six tokens, which is exactly why connections exist as a first class thing here.

githubfine grainedtokenresource owner
30 Should I use a GitHub App instead?

For anything permanent, yes. Its tokens skip the single sign on authorization step that catches out personal tokens and it does not belong to a person who might leave. The one limitation today is that the scanner works with one installation at a time.

github appssoautomation
31 How do I connect GitLab?

A group or project access token with read_api and read_repository. Groups nest, so put the top group in the scope and everything under it is covered. Works against gitlab.com and self managed.

gitlabtokengroup
32 How do I connect Bitbucket?

An app password on Bitbucket Cloud, or an HTTP access token on Data Center. Repository read and account read are the two permissions needed.

bitbucketapp passwordtoken
33 How do I connect Azure Repos?

A personal access token with Code read. Both the organization and the project go in the scope, because Azure nests one inside the other.

azuredevopstoken
34 AWS CodeCommit says a tool is missing.

It needs the AWS command line tools present, because cloning uses the credential helper they provide rather than a token in a URL. It refuses by name rather than reporting an empty estate, which is the failure that wastes an afternoon. Google Cloud Source Repositories needs gcloud for the same reason.

awscodecommitgcloudgoogletrouble
35 What works and what does not for Subversion and Perforce?

Neither is git, so there is no commit graph to walk and the credential history pass does not apply. Everything that reads files works: the code analysis, the dependency tree, the licenses, the infrastructure files and the triage. The connection form says so before you set one up.

subversionsvnperforcelimits
36 How do I check a connection before I trust it?

Press Test. It proves the credential works, reports which permissions it actually has rather than the ones you asked for, and lists the first few repositories it can see. If that list is shorter than expected, stop and find out why before scheduling anything.

testpermissionsverify
37 Why should I set sensitivity on a connection?

Because it is fifteen minutes that improves the ranking of every finding that connection ever produces. Labels set here are inherited by every repository under the connection unless that repository says otherwise, and they decide which exceptions and severity overrides apply.

sensitivitylabelsrankingtagging

Running scans

Starting one, scheduling them, and what to do when one stops.

14
38 How do I run my first scan?

Open Scan history, press New scan, choose one connection and one organization, and start it. Start small: the first scan of one organization tells you more about how long your estate takes to read than any estimate will, and it does it in twenty minutes rather than six hours.

first scanstartgetting started
39 How long does a scan take?

It is dominated by cloning, so it depends on your estate and your network rather than on the machine. A few dozen small repositories is minutes. A hundred is under an hour. Four hundred across several hosts is a couple of hours, which is why it belongs overnight.

durationperformanceslow
40 How do I put scans on a schedule?

One schedule per organization, overnight, staggered by an hour or so. That spreads the load on your network and on the host rate limits, and stops one slow organization holding up the rest of the night.

schedulecronnightly
41 Can two scans run at once?

Yes. The limit is a setting and it defaults to one, so nothing changes for anybody who does not want it. What cannot happen is two scans of the same connection at the same time: the second is refused and names the one already running. Before that they both ran, cloned the same repositories twice and wrote the same rows twice.

The rate limit budget is tracked per connection, so runs on one credential do not each assume they have the whole thing.

concurrencyparallelrate limitduplicate
42 How do I stop a running scan?

Each running scan has its own stop button on its own row in Scan history. What it has already finished is kept.

stopcancel
43 A scan was interrupted. Did I lose everything?

No. Repositories it had already finished are ingested and their findings appear. Repositories it never reached are left exactly as they were, and nothing in them is counted as fixed. The run is marked interrupted rather than finished, so the history does not claim a clean result.

interruptedrestartdeployrecovery
44 What is the most common cause of an interrupted scan?

A deploy. Recreating the container during a scan stops it. Checking Scan history before restarting anything is a habit worth building, because a scan ninety minutes in is ninety minutes of cloning to do again.

interrupteddeploydocker
45 Should I scan every branch and the full history?

Yes, and it is the default. A credential deleted from a file last March is still in the commit that added it, and that is where most of what a first scan finds actually lives. Shortening the history is how you stop finding the things that matter most.

brancheshistorysettings
46 The scan says temporary files are going to disk.

The container does not have enough shared memory, so the scan fell back to disk. Give it a gigabyte in the compose file. Until then the cleartext of found credentials is being written to a disk on its way to becoming a hash.

shmmemorysecuritytrouble
47 A whole category of finding is missing.

An engine is absent, or a toolchain it needs is absent. Several engines exit cleanly having found nothing when that happens, which looks identical to a clean result. The scanner refuses rather than reporting that, and names the engine in the log. This is the failure to check first when the numbers look too good.

enginemissingsilenttrouble
48 The scan is using almost none of the machine.

Probably correct. The work is capped by Repositories at once and not by the size of the host, so on a large machine a scan running flat out still leaves most of it idle. A load average of five on a sixty four core box is a scan working, not a scan asleep.

It also alternates. Cloning and running the engines is processor bound. Collecting access and the rest of the API work is one request at a time and uses almost nothing. Sampling during the second of those and concluding the scan has stopped is the usual mistake.

cpuloadperformanceidleslow
49 A scan has done nothing for half an hour. Is it stuck?

Look for a sleep. A worker waiting out a rate limit is a sleep process with the seconds left on it, and that is the scan behaving correctly rather than hanging. Ignore the long ones matching the per repository time limit: those are watchdogs, and there is one for every repository in flight.

It is genuinely stuck only when the scanner is alive with no clone, no request, no engine and no sleep. Nothing reaches the log while a worker waits, so silence on its own says nothing either way.

stuckhangbackofftroublesilent
50 GitHub is throttling the scan.

That is the secondary limit. It is separate from the five thousand requests an hour and does not show up in that budget, so the counter can read plenty left while every worker is waiting. It triggers on how many requests arrive together, which makes Repositories at once the setting that causes it.

Lower it. A high value finishes ordinary accounts a little sooner and then loses far more to one penalty, which arrives as a wait of about forty minutes and applies to the whole token. One avoided penalty pays for a dozen slightly slower accounts.

rate limitsecondarythrottleparallelslow
51 One repository has a thousand branches and never finishes.

Set Skip branches older than to a hundred and eighty days. Most of a count like that is feature branches merged years ago, and a good deal of the work is per branch. The default branch is kept whatever its age, so a quiet master is not dropped by the cutoff.

Most branches per repository looks like the same tool and is not. It cuts by position rather than by age, so it can remove a branch somebody is working on today. Reach for the age first and leave the cap alone.

branchesslowstalelimitsperformance

Working the list

What the list is, and where to start on it.

8
52 The first scan found nine thousand things. Where do I start?

Not at the top of the list. Four questions first, each about a minute:

  1. Is anything verified? Those are live keys and they are today's work.
  2. Is anything on the exploited list? A medium being used beats a critical nobody has used.
  3. How much of this is historic? If nearly all of it predates last year, set a baseline.
  4. What is the shape of the noise? Group by rule; the top ten are usually half of everything.
first scantriageoverwhelmednoise
53 Why is the list not sorted by severity?

Because severity is not the same as what is worth doing first. A credential the issuing provider has just confirmed still works outranks a critical in a package nothing calls. The default order is the ranking; you can sort by severity if you want it.

rankingsortseverity
56 Can I send somebody a filtered view?

Yes. Every filter is in the web address, so a view can be copied and pasted and it will be the same view when they open it. That is the difference between "have a look at the findings" and "have a look at these eleven".

filtershareurl
57 What does the finding page show me?

The evidence, the reasoning behind every judgement the product made, the context of the repository it is in, who can reach that repository, and every decision available to you. There is no second click to find out why something was graded the way it was.

detailevidencecontext
58 What does "first seen" actually mean?

The first scan that reported this finding, which is not the same as when the problem was introduced. For credentials the commit that introduced it is also recorded, which is the honest answer to how long the exposure has existed.

first seenagehistory
59 A finding I fixed keeps coming back.

It is in the history rather than in the current files. Deleting a secret from a file does not remove it from the commit that added it. The finding says whether it is live on a branch or history only. If it is history only, the fix is a rotation plus a decision about the history, and an exception with a reason is a perfectly reasonable answer.

historyrecurringfixedtrouble

Credentials

Leaked keys: what is stored, what is verified, what to do.

8
60 What counts as a credential here?

Anything that would let somebody act as you: API keys, cloud access keys, connection strings with a password in them, private keys, tokens, webhook URLs that carry their own authentication, and plain username and password assignments in code.

secretscredentialskeys
61 Does it store the secrets it finds?

No. The cleartext exists only inside the running scan. Before anything is written down it becomes a SHA-256 fingerprint and a mask showing the first four characters and the last three. The product genuinely cannot show you a secret it found, to anybody.

storageprivacymaskingfingerprint
62 What is live verification?

The candidate credential is sent to the service that issued it, using that service's own token introspection endpoint, and the answer is recorded. A verified finding outranks everything else on the list. It is the strongest signal the product produces.

verificationliveverified
63 How do I turn live verification off?

It is a setting, and on the command line it is --no-verify. It is the only case where a candidate value goes anywhere, so some environments will not allow it. Everything else still works with it off.

verificationprivacyofflineno-verify
64 Will it revoke a leaked key for me?

No, and it never will. Revoking is a write into somebody else's account and getting it wrong takes a production service down at an unpredictable moment. The finding tells you which key it is, where it is, whether it still works and who can reach it. Pressing the button is yours.

revokerotatelimits
65 I found a live key. What is the order of operations?
  1. Issue a replacement and deploy it, so the rotation does not take a service down.
  2. Revoke the old one at the provider.
  3. Check the provider's access logs for use you cannot account for.
  4. Then decide about the history, which is the slow part and does not block the first three.
rotateincidentrunbooklive
66 How do I exclude one credential everywhere it appears?

Copy the fingerprint from the finding, then create an exclusion with type credential and that fingerprint, leaving organization, repository and path as *. Two findings with the same fingerprint are the same secret wherever they are, so one decision covers all of them, including the ones nobody has found yet.

exclusionfingerprintbulk
67 Does the cred: prefix on a fingerprint matter?

No. A rule written as f960f486dcc3fe6d and one written as cred:f960f486dcc3fe6d both match. Rules written by hand and rules carried over from an older installation disagree about it, and neither should silently fail.

fingerprintexclusionprefix

Code findings

Static analysis, confidence, and where it falls short.

4
68 Which engines read the code?

semgrep across thirteen rule packs, on whichever languages are present. gosec is added for Go and bandit for Python, because both analyze more than one file at a time and can follow a value semgrep cannot see past.

semgrepgosecbanditsast
69 What is the fidelity pass?

Code findings are read back in their own context before anybody sees them: a value cast to an integer cannot carry markup, an input from a fixed list is not user input, a header set earlier changes what a later call does. Each verdict carries the sentence that justifies it, so you can disagree with the argument rather than the answer.

triagefalse positiveconfidence
70 Why are so many high findings low confidence?

Because a lot of rules exist to say "somebody should look at this" rather than "this is a bug". On the corpus used for the documentation, sixty two per cent of high and critical code findings came from rules the engine itself marks as low confidence. They are marked, and can be regraded as a group.

audit rulesconfidencenoisetuning
71 What is the biggest weakness in the code analysis?

Free semgrep reads one file at a time, so a tainted value crossing a function boundary into another file is invisible to it. gosec closes this for Go and bandit helps for Python. For Java, PHP and Ruby it is open, and it is the clearest gap in the product.

limitsjavaphprubytaint

Dependencies

Bills of material, reachability and malicious packages.

7
72 Where does the dependency data come from?

syft builds a bill of materials from what is actually in the checkout rather than from what a manifest claims, then grype and osv-scanner run against that. Two of them, because they draw on overlapping but different databases.

sbomsyftgrypeosv
73 What is reachability?

Whether your application can call the vulnerable package at all. Three questions: is it a direct dependency, is it a development dependency that never ships, and is the vulnerable module imported anywhere in the source. On most estates that marks three quarters of dependency findings as not reached, each with a sentence saying why.

reachabilitynoisedependencies
74 How accurate is reachability?

It is a heuristic and it says so on every finding. When it cannot tell, the verdict is unknown and the finding keeps its place, because a wrong "unlikely" is somebody not looking at the one that mattered. A commercial product with a real call graph makes a stronger claim than this does.

reachabilitylimitsaccuracy
75 What are KEV and EPSS?

KEV is a published list of vulnerabilities known to be exploited in the wild. EPSS is a published score for the probability of exploitation in the next thirty days. Both are on every dependency finding, and together they are what separates the criticals nobody will ever use from the handful that will.

kevepssexploitedranking
76 What is a malicious package?

Not a package with a hole in it: a package that is the attack. A typosquat, or a real package whose maintainer account was taken over. Most of them run at install time, so reachability is not a mitigation and a malicious package is never recommended for a lower grade.

malicioustyposquatsupply chain
77 How does it find malicious packages with no advisory?

Four signals checked against your own dependency files: an install hook that fetches and runs something, a dependency pointed at a git URL or tarball rather than a registry, a version specifier with no upper bound, and a name one edit away from a popular one. All four are suspicions and are graded as suspicions.

maliciousinstall hookdependency confusion
78 Does it scan container images?

It reads the base image every Dockerfile names, pulls it from its registry and reads it, with nothing built and no daemon. It is off by default because it downloads. On most applications the operating system layer holds more criticals than the application dependencies put together.

containersdockerbase image

Triage and tuning

Turning nine thousand findings into a morning of work.

6
79 What are triage rules?

The rules that decide whether a string that looks like a credential is a credential or an example. Twenty ship, every one can be switched off, and an administrator can add more by value or by path. There is a box on the page to test a candidate before saving.

triagerulesplaceholderfalse positive
80 Why can I not test a value rule against past findings?

Because the cleartext never leaves the scan. There is nothing stored to run a new pattern against retroactively. The test box takes a value you paste in and shows what would match, which is the only preview that is possible.

triagetestmasking
81 How do I restore the default triage rules?

Delete your overrides. The shipped rules are read from the product, not the database: a row exists only where somebody made a decision. That is what makes restoring the defaults a matter of deleting rows rather than remembering what they were.

triagedefaultsreset
82 One rule is producing a fifth of my findings. What do I do?

Decide once what it is worth. If it is right but graded too high for your context, regrade it in Custom severity. If it reports a pattern nobody will act on, use a rule mute. If it is only wrong in some places, write an exclusion scoped to those paths. Whichever you choose, write the reason.

noisetuningmuteseverity
84 How long should tuning take?

About an hour, once, with somebody who knows the estate in the room. The alternative is tuning one finding at a time over three months, which produces a long tail of exceptions with no reason written on them and a tool that gets switched off in the second month.

tuningfirst hourprocess

Exceptions

Setting things aside, with a name and a reason on each.

7
85 What is the difference between an exception, an exclusion and a mute?

An exception covers one finding somebody looked at. An exclusion is a rule that covers whatever matches it, now and in future. A rule mute sets aside everything one rule will ever produce across the whole estate, and needs a second administrator to agree. None of the three delete anything.

exceptionexclusionmutewaiver
86 Who can approve an exception?

An administrator. A developer can ask for one on anything they can see, and nothing changes until it is approved. An administrator asking for one approves it in the same step, and may approve an exception they asked for themselves.

approvalrolesworkflow
87 Do exceptions expire?

Yes, and the date is required, up to a year out. When it passes the finding comes back into the work list and into the report files on the next write. A monthly ten minutes on what expires in the next thirty days is much easier than forty findings reappearing on the same morning.

expiryreviewprocess
88 Does an excepted finding disappear from the reports?

No. It leaves the work list and stays in the record, marked as excluded with the reason and the reviewer attached. An auditor reading the output sees both what was found and what was decided about it.

reportsauditevidence
89 Can an exclusion be scoped so it does not apply everywhere?

Yes. An exclusion can say what it was not written for. "This is fine except where the repository holds cardholder data" means the finding comes back on those systems, with the reason it was let through elsewhere still on the record.

sensitivityscopeexclusion
90 Why does a rule mute need two people?

Because it hides findings nobody has seen yet, estate wide, with no expiry, and the person switching it on will not be the person who discovers what it hid. Everything else in the product hides findings somebody has looked at.

muteapprovalgovernance
91 What is a baseline?

A date, per organization. Findings older than it are still counted and still in the reports, but they are out of the work list. It turns a backlog into a project that can be planned separately, instead of a wall that makes people close the tab.

baselinebackloghistoric

Context

What each repository is, what it holds and who can reach it.

11
92 What is tagging for?

Recording what each repository is: the application it belongs to, both owners, the environment, what data it holds and a sentence describing it. What it holds is the field that changes ranking, because the same credential in a payment system and a brochure site are not the same problem and no scanner can work that out from the code.

taggingsensitivitycontextranking
93 How do I tag two hundred repositories without going mad?

Export the CSV from the Tagging page, fill in what you know, import it back. The answers usually already exist in an asset list somewhere, and a form two hundred times is data entry rather than work.

csvbulkimportexport
94 What are the rules of the tagging import?
  • It updates. It never creates a repository, because a repository here means one the scanner actually found.
  • An empty cell leaves the existing value alone, so a partial spreadsheet is safe.
  • A single dash clears a value.
  • Several values in one field are separated by a pipe: Cardholder data|Personal data.
  • It does not stop at the first bad row. It reports what it could not use and processes the rest.
csvimportpiperules
95 What is blast radius?

Who can reach each repository and what would be reachable through it: read and write counts, outside collaborators counted separately, whether the default branch is protected, which application it belongs to and who owns it. Clicking a row opens the description and what it holds.

blast radiusaccessexposure
96 Why are outside collaborators counted separately?

Because forty employees with write access is a governance question and four contractors with write access to the payment service is a different question. Hiding both inside one number answers neither.

accesscollaboratorsrisk
97 The access numbers look wrong or empty.

The access inventory needs a credential that can read organization membership. Without one the page shows what it can and says it is incomplete rather than showing a confident zero. It also cannot see access granted outside the git host.

accesspermissionstrouble
98 What is the info section on a finding?

The repository description, what it holds, the application, both owners and who has access, shown beside the finding itself. Somebody reviewing a finding has to answer "how bad is this here", and that cannot be answered from the code.

findingcontextsensitivity
133 What does a certified branch mean, and who can certify one?

It means somebody in security read that branch and wrote down what they read and what they concluded. It covers one branch of one repository, it carries an expiry, and it goes amber the moment anybody pushes, because a reviewer read some code and different code has not been read. Certifying is an administrator action and it is done from a finding, where the repository, the branch and the commit are already in front of you.

It hides nothing. A certified repository that starts producing criticals shows every one of them, next to a badge saying the review no longer covers what is there.

certificationsign offreviewbranch
134 Why are branches being certified without anybody clicking anything?

Because most branches in an estate carry nothing anybody would open, and asking a person to sign each of them off produces a queue nobody works through. A completed scan signs off the branches it found nothing to read on: no findings at all, or nothing above a low.

Those badges say a scan wrote them wherever they are shown, and reports count them apart from the ones a person reviewed. A scan will never write over a sign off somebody made, never re-date one, and never revive one that was withdrawn.

certificationautomaticcoveragebranch
135 A scan finished and nothing was certified. Why?

The likeliest reason is that the scan did not finish everything. A branch is only signed off automatically when the scanner reports that all four of its passes completed for that repository, because a pass that did not run reports nothing and nothing is exactly what a clear branch looks like. A repository whose worker died, whose code pass was cut short, or whose credential history walk covered only a window is left alone.

The other reasons are ordinary: the branch holds a finding above low, somebody marked it as not expecting a review, a person already signed it, or a person withdrew a badge on it.

certificationautomaticpassestroubleshooting
136 How is certification coverage worked out?

As a share of branches, not of repositories and not of certifications. A count of certifications only ever goes up and tells nobody how much is left. The branches counted are the ones scans have actually walked, so a repository nobody has scanned contributes to neither side of the figure: that is a scan somebody owes rather than coverage somebody owes.

Archived repositories are left out entirely, and so are branches somebody has marked as not expecting a review. A total that includes work nobody intends to do measures the wrong thing.

certificationcoveragemetricsbranch

Notifications

Slack, Teams, mail and webhooks, without training people to ignore them.

10
99 What can it notify?

Slack, Microsoft Teams, mail by SMTP, mail through the Microsoft Graph API, and a plain JSON webhook for anything else.

slackteamsemailwebhook
100 Why does it only notify about new findings?

Because a nightly message about the standing backlog teaches people to filter the sender, and once they have, the message that mattered goes into the same folder. Every finding carries when it was first seen, so new is already answerable.

notificationsnoisedigest
101 What gets sent immediately rather than in the digest?

Three things: a credential the provider has just confirmed still works, a vulnerability on the exploited list, and a malicious package. Everything else above your chosen floor goes out as one digest at the end of the scan.

alertsurgentdigest
102 My SMTP relay does not want a password.

Then do not give it one. Both the username and the password are optional, and leaving them empty means no authentication is attempted rather than an empty one being sent. Plenty of internal relays accept mail from known hosts by address and have no accounts at all.

smtprelayno passwordmail
103 The relay refuses our mail.

It is nearly always the source address rather than the message. The host running the container is what needs to be on the allow list, and inside Docker that address may not be the one you expect.

smtprelaytrouble
104 Our tenant has SMTP authentication turned off. Can we still send mail?

Yes, through the Microsoft Graph API. Register a single tenant application in Entra ID, take the tenant id and client id, create a client secret, add the application permission Mail.Send for Microsoft Graph, and press Grant admin consent. Then enter the four values in Notifications and press Send a test.

microsoft 365graphentraazuremail
105 Graph mail is not working. What did I miss?

Almost always the admin consent step, which is easy to skip and gives an unhelpful error. After that: a delegated permission where an application permission was needed, or a from address that is not a real mailbox. The test button distinguishes the four cases.

graphconsenttroublemail
106 Should I restrict the Graph permission?

Yes. Mail.Send as an application means every mailbox in the tenant unless you restrict it. An application access policy scoped to the one mailbox takes a couple of minutes in PowerShell.

graphsecurityleast privilege
108 Do notifications contain the secret?

No, only the same masked preview the reports carry. This matters because a notification lands somewhere with a different set of readers than the product has: a Slack channel is often much more widely visible, and mail gets forwarded.

securitymaskingslack

In your editor

The plugin for VS Code, Cursor, JetBrains and Eclipse, and where it comes from.

7
16 Is there a plugin for my editor?

Visual Studio Code and Cursor install the same package, and the JetBrains IDEs and Eclipse run the language server underneath it, through LSP4IJ and LSP4E. Neovim, Helix and Sublime Text run that same server. One language server does the work everywhere rather than a plugin per IDE, which is how this kind of thing usually dies: four codebases, and a fix that lands in one.

vscodecursorjetbrainsintellijeclipsepluginlsp
17 Where do I download it?

From the Editor plugins page of your own server, not from a marketplace. That server is the one holding your rules, and plenty of these installations have no way out to the internet. The package carries the version of the server that handed it over, and building the same release twice gives the same bytes, so the checksum on the page is one you can compare against what you downloaded.

downloadvsixmarketplaceofflineair gapped
18 Does my code leave the machine?

The file you are editing goes to your own Git Code Review server, is written to a temporary directory there, read by the same engines a scan uses, and deleted. Nothing is stored: a review creates no finding, no exception and no scan, which is why it is safe to run on every save and why a stolen token cannot change anything.

privacyself hostedreviewstorage
19 Why am I only seeing findings on some lines?

Because that is the default, and it is the setting worth keeping. Only lines that differ from HEAD carry a finding, and the status bar says how many older ones are in the file. A plugin that opens showing two thousand findings is uninstalled that afternoon, and none of those two thousand are yours. Turn it off with gitCodeReview.changedLinesOnly.

changed linesnoisesettings
20 It says command not found: code.

Nothing is wrong with the download. That command is the editor's command line helper and it is not always on your PATH. On a Mac it never is until you ask for it: press Cmd+Shift+P to open the editor's search box, run Shell Command: Install 'code' command in PATH, then open a new terminal. Cursor has the same entry for cursor. On Windows it means the installer ran without Add to PATH ticked, or the window was open before the editor was installed. On Linux the packaged builds add it and an AppImage does not.

You do not need it either way. Press Ctrl+Shift+P, or Cmd+Shift+P on a Mac, to open the search box the editor calls its command palette, then Extensions: Install from VSIX and pick the file. That is the same job and it needs nothing on your PATH.

codecommand not foundpathvsixmacostrouble
21 It says SSL alert number 40, or a handshake failure.

The editor could not set up a secure connection at all, before any certificate was looked at. Three things cause it, and they are told apart in a minute:

  • The address begins https:// and the server speaks plain http. Typing an address with no scheme is the usual way in, because https is then assumed. Run Git Code Review: Check the connection, which asks the server both ways and says so plainly.
  • The server has no certificate for the name used. An address written as an IP rather than a hostname does this, because the server is never told which site was asked for.
  • Something between the two is inspecting TLS, which a corporate proxy does by design. Compare with curl from the same machine: if curl works and the editor does not, it is the editor's proxy settings rather than the server.

curl -sS -o /dev/null -w "%{http_code}\n" https://your-server/api/v1/ping -H "Authorization: Bearer <token>"

ssltlshandshakealert 40eprotoproxytrouble
22 It says the token was refused.

An editor token needs review and rules both ticked. One with only review cannot read the rules and one with only rules cannot ask for a review, and the server answers both cases the same way on purpose. Run Git Code Review: Check the connection, which names the case it can tell apart.

tokenscopes401trouble

Pipelines

Failing a build, SARIF, and catching things before the commit.

5
109 How do I fail a build on findings?

Run the scanner in the pipeline and use the exit code. Start with credentials only:

ghscan review . --diff origin/main --fail-on secrets

Failing on any critical, in a repository with a backlog, fails every build from the first minute and gets switched off by the end of the week.

cipipelinefailexit code
110 What are the exit codes?

0 completed and nothing met the threshold. 1 something went wrong. 2 bad usage. 3 completed and findings met the threshold. So a pipeline can tell "we found things" apart from "the scanner broke", which matters.

exit codecipipeline
111 How do I check only what this branch changed?

--diff origin/main. The review is limited to the files changed against that reference, so the pipeline reports what somebody wrote rather than the repository's whole backlog. That is the difference between a check people fix and a check people bypass.

diffpull requestci
112 How do I get findings into a Security tab?

Write SARIF and let the pipeline upload it:

ghscan review . --format sarif > findings.sarif

The upload uses the pipeline's own token, which is how annotations appear on a pull request without this product ever holding write access to anything.

sarifgithubannotationsupload
113 Can I check a file before it is committed?

Yes, two ways. ghscan review PATH in a pre commit hook, which clones nothing and needs no token. Or the editor API, which takes files as text over JSON and returns findings. There is an API and no editor plugin yet.

pre-commiteditorideapi

Reports and evidence

The files, the schema, and answering a customer.

7
114 What files does a scan write?

findings.json, findings.csv, findings.sarif, a CycloneDX and an SPDX bill of materials, both VEX dialects, excluded.json and summary.json. All of them are on the Report files page.

reportsjsoncsvsarifsbom
115 Do the files stay in step with the screen?

Yes. They are rewritten whenever a decision changes what is reportable, not only when a scan runs, and they are written atomically so a reader never sees half a file.

reportsconsistencyatomic
116 Is the report format documented?

The JSON report validates against a JSON Schema that ships with the product, and the scanner can check a report against it:

ghscan validate --report ./out/findings.json

If you are building something on these files, validate in your own tests.

schemajsonintegration
117 A customer says our product contains a vulnerable library. What do I send them?

The bill of materials and the VEX document, together. One says what is in the product, the other says which of it matters. Both are written on every scan, in both standard formats.

vexsbomcustomerquestionnaire
118 What goes into a VEX document?

Only decisions somebody made: an exception with a reviewer and a reason, or a triage verdict of not applicable with the sentence explaining it. "The scanner did not look" is not a statement anybody should make in that format, so nothing else is emitted.

vexopenvexcyclonedx
119 Is an accepted risk emitted as not affected?

No, never. A waiver that says "we will fix this next quarter" is emitted as affected, with the reason attached. Saying otherwise tells a customer they are safe when the decision was "later", in a format designed to be trusted automatically.

vexhonestyaffected
132 The certification badge turned amber.

Somebody pushed. The commit that was reviewed is recorded and compared against where the branch is now, so any new commit turns the badge amber whether or not it produced a finding. Waiting for a finding would be the wrong test: a reviewer read some code, and different code has not been read.

Amber is shown and not enforced. Nothing is hidden, no grade moves and nothing leaves a report. Re-certify once somebody has read what changed, and the diff since the recorded commit is the thing to put in front of them.

certificationamberstaleevidencesign off

Users and access

Roles, scopes, impersonation and the audit log.

6
120 What are the roles?

Three. A reader can read and nothing else. A developer sees only the organizations and repositories assigned to them and can ask for an exception. An administrator sees everything, approves exceptions, changes severities, manages users and reads the audit log.

rolesrbacpermissions
121 A new developer sees an empty screen.

That is deliberate. A developer with nothing assigned sees nothing until somebody decides what they should see. Assign an organization or specific repositories in Users and access.

rolesscopedeveloper
122 Can a developer see another team's findings by guessing a URL?

No. The scope is enforced in the database query rather than in the page, and a lookup outside scope returns a not found rather than a forbidden, so the answer tells them nothing about whether it exists.

securityrbacidor
123 How do I see what another user is seeing?

Users and access, then View as. You see exactly what they see, a band across the top says so, and every write is refused at the middleware before any route runs. Both the start and the end are in the audit log with both names on them.

impersonationview assupport
124 What is in the audit log?

Sign ins, exceptions asked for and approved, severity overrides, exclusions and triage rules, settings changes by name, scans started and stopped naming the connection, impersonation at both ends, and every change to a user account. It filters as you type and every event opens to show its full detail.

auditcomplianceevidence
125 Are secrets ever written to the audit log?

No. A settings change is recorded by name, never by value. The same applies to logs and to the API: a stored secret is never returned or shown back once saved.

auditsecretssecurity

When something is wrong

Backups, logs, and the things that actually go wrong.

6
126 What should I back up?

The database, the installation secret and the .env file. The database holds every finding and decision and the encrypted connection credentials; the secret is what decrypts them, so keep it somewhere else. The report files regenerate on the next scan.

backuprestoredisaster recovery
127 How sensitive is the database backup?

Very. It holds the encrypted credentials for reading every repository you own and a map of everywhere a secret has leaked. Treat it like a backup of a password vault, and do not put it somewhere with a wider audience than the product itself.

backupsecurityrisk
128 A scan will not start.

Either another is running and the concurrency limit is one, or the previous run was interrupted and has not yet been marked as such, which happens on the next boot. Scan history shows both states.

scanstucktrouble
129 Where are the logs?

The log excerpt for a run is on the run in Scan history, including a traceback if it failed. Full logs are in the container. Grep for the word warn first, because that is where the silent failures announce themselves.

docker compose logs --tail 500 github-code-review | grep -i warn
logsdebugtrouble
130 How much does it cost?

Nothing. MIT licensed, no paid tier, no seat count and no edition with the useful parts taken out. There is also no support to buy: bugs and questions go to the repository.

licensepricefreesupport
131 Does it send anything about our code anywhere?

No. No telemetry, no analytics, no license check and no phone home. The only outbound traffic is to your own git hosts, to the public vulnerability databases which are asked about package names and versions, and to a credential's own issuer when live verification is on.

privacytelemetryoffline

Longer, worked guides

17 of the jobs above are worth more than a paragraph. These are written as steps, with the commands.

Install the editor plugin on Windows

VS Code, Cursor, IntelliJ and Eclipse, from your own server rather than a marketplace.

14 steps

Install the editor plugin on macOS or Linux

The same three files, with the paths and the package manager that platform uses.

15 steps

Exclude one credential everywhere it appears

The same test key committed to nine repositories, set aside once.

5 steps

Stop a rule that is drowning everything else

One rule producing a fifth of your findings is not telling you anything.

5 steps

Put the scanner on your build server

The step every pipeline recipe skips: how the checking tool reaches a machine that is not this one.

9 steps

Fail a build, without failing every build

Block on what this change introduced, not on the backlog behind it.

5 steps

Send mail through Microsoft 365 with no SMTP

For tenants that have turned basic authentication off, which is most of them now.

7 steps

Use an internal relay that wants no password

A whitelisted relay authenticates the host, not an account.

5 steps

Tag two hundred repositories without a form

Export the spreadsheet, fill in what you know, import it back.

5 steps

Find out which repository got worse this month

Three clicks from a rising line to the eleven findings behind it.

6 steps

Sign off a branch, and let the scans do the rest

A review is a record with a name, a date and an expiry on it. Most branches never need one.

6 steps

The first hour with a list of nine thousand findings

What to do before anybody looks at the list, so that they will.

6 steps

Scan GitHub organizations behind single sign on

And notice when the token is only authorized for some of them.

5 steps

Answer a customer asking about a vulnerability in your product

With a file, in a format their scanner reads, rather than an email thread.

5 steps

Put it behind a reverse proxy with TLS

It listens on loopback and speaks plain HTTP on purpose.

4 steps

Reproduce what another user is seeing

Without asking for their password or guessing at their role.

4 steps

Work out what happened to an interrupted scan

And know what it kept.

5 steps