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.
Installing
Getting it onto a server and running for the first time.
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.
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.
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.
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.
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.
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.
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.
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.
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.
Signing in
Passwords, authenticator apps, single sign on and getting back in.
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.
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.
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.
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.
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.
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.
Connections
Reaching your code, on twelve kinds of host.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
Running scans
Starting one, scheduling them, and what to do when one stops.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
Working the list
What the list is, and where to start on it.
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:
- Is anything verified? Those are live keys and they are today's work.
- Is anything on the exploited list? A medium being used beats a critical nobody has used.
- How much of this is historic? If nearly all of it predates last year, set a baseline.
- What is the shape of the noise? Group by rule; the top ten are usually half of everything.
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.
54 How do I get from a number on the trends page to the rows behind it?
Two ways, and they answer different questions. Drill down stays on the trends page and narrows it: the estate opens into its organizations, an organization into its repositories, a repository into its branches, with the chart and the risk matrix narrowing at each step. That is still the question "how much". Findings leaves the page and opens the list of the rows themselves, which is the question "which ones".
Every figure in the grid is a link as well, and the list it opens holds exactly what the figure counted: every state, every exception, ungrouped. There is nothing below a branch, so a branch row offers only the findings.
55 Can I sort the trends grid by one severity?
Yes. Every column heading sorts on itself and clicking it again turns it around, with an arrow saying which way it is reading. The default is worst first by grade, which is the right answer to "who is in the most trouble" and the wrong answer to "who has the most lows".
The sort and the paging both run over the whole set rather than the rows on screen, which matters on an account holding three hundred repositories: sorting a page you were already given looks identical and answers a different question.
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".
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.
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.
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.
Credentials
Leaked keys: what is stored, what is verified, what to do.
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.
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.
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.
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.
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.
65 I found a live key. What is the order of operations?
- Issue a replacement and deploy it, so the rotation does not take a service down.
- Revoke the old one at the provider.
- Check the provider's access logs for use you cannot account for.
- Then decide about the history, which is the slow part and does not block the first three.
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.
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.
Code findings
Static analysis, confidence, and where it falls short.
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.
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.
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.
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.
Dependencies
Bills of material, reachability and malicious packages.
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.
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.
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.
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.
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.
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.
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.
Triage and tuning
Turning nine thousand findings into a morning of work.
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.
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.
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.
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.
83 What is a recommended severity?
When the evidence already on a finding contradicts its grade, the page says so and offers a lower one, in words rather than a number. Nothing changes until an administrator accepts it, which is one click and lands in the audit log. It will never soften a malicious package, a verified credential or anything on the exploited list.
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.
Exceptions
Setting things aside, with a name and a reason on each.
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.
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.
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.
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.
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.
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.
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.
Context
What each repository is, what it holds and who can reach it.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
Notifications
Slack, Teams, mail and webhooks, without training people to ignore them.
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.
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.
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.
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.
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.
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.
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.
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.
107 Links in Slack and mail go nowhere.
The reachable address field is empty or wrong. The product has no way to know what address people use to reach it from outside, so there is a field for it, and if it is wrong every link in every message is wrong.
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.
In your editor
The plugin for VS Code, Cursor, JetBrains and Eclipse, and where it comes from.
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.
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.
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.
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.
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.
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 plainhttp. Typing an address with no scheme is the usual way in, becausehttpsis 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
curlfrom 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>"
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.
Pipelines
Failing a build, SARIF, and catching things before the commit.
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.
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.
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.
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.
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.
Reports and evidence
The files, the schema, and answering a customer.
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.
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.
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.
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.
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.
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.
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.
Users and access
Roles, scopes, impersonation and the audit log.
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.
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.
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.
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.
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.
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.
When something is wrong
Backups, logs, and the things that actually go wrong.
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.
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.
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.
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
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.
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.
Nothing matched that.
Try fewer words, or a word that would appear in the answer rather than the question. The feature pages go into more depth, and the documentation covers the parts that need a whole chapter.
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 stepsInstall the editor plugin on macOS or Linux
The same three files, with the paths and the package manager that platform uses.
15 stepsExclude one credential everywhere it appears
The same test key committed to nine repositories, set aside once.
5 stepsStop a rule that is drowning everything else
One rule producing a fifth of your findings is not telling you anything.
5 stepsPut 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 stepsFail a build, without failing every build
Block on what this change introduced, not on the backlog behind it.
5 stepsSend mail through Microsoft 365 with no SMTP
For tenants that have turned basic authentication off, which is most of them now.
7 stepsUse an internal relay that wants no password
A whitelisted relay authenticates the host, not an account.
5 stepsTag two hundred repositories without a form
Export the spreadsheet, fill in what you know, import it back.
5 stepsFind out which repository got worse this month
Three clicks from a rising line to the eleven findings behind it.
6 stepsSign 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 stepsThe first hour with a list of nine thousand findings
What to do before anybody looks at the list, so that they will.
6 stepsScan GitHub organizations behind single sign on
And notice when the token is only authorized for some of them.
5 stepsAnswer a customer asking about a vulnerability in your product
With a file, in a format their scanner reads, rather than an email thread.
5 stepsPut it behind a reverse proxy with TLS
It listens on loopback and speaks plain HTTP on purpose.
4 stepsReproduce what another user is seeing
Without asking for their password or guessing at their role.
4 stepsWork out what happened to an interrupted scan
And know what it kept.
5 steps