Repository Security Assessment Checklist
Published · Strata Security
1A Checklist, Not a Scan Report
An automated repository scan tells you what's wrong with the code and its dependencies right now. It doesn't tell you who has admin access to the repository, whether that access has been reviewed since the last three people left the team, or whether the repository even has an owner anymore. This checklist covers the assessment work that happens around a scan — the process and access questions a tool can't answer just by reading the code.
2Permissions and Access
- List every collaborator and team with write or admin access — not just the ones you remember granting recently.
- Confirm automation (CI/CD, bots, integrations) uses a scoped service account or app installation, not a personal access token tied to one employee's account.
- Check for admin access granted to former contractors or employees whose offboarding didn't include a repository access review.
- Apply least privilege deliberately: does this collaborator need write access, or would read plus a review requirement be enough?
- Set a recurring review cadence for access — quarterly is a reasonable default for anything handling production code.
Excess standing access maps to CWE-284 (Improper Access Control) at the repository level — the same weakness class as an over-broad application permission, just applied to who can push code rather than what the code itself can do at runtime.
3Branch Protection
Branch protection is a large enough topic to deserve its own treatment — see CI/CD Security Controls Every Engineering Team Should Have for required reviews, required status checks, and force-push restrictions. For this checklist, the short version: confirm protection rules exist on every branch that actually deploys, not just on main by convention.
4Dependency Review
- Confirm a lockfile exists and is committed — without one, "the same code" can resolve to different dependency versions on different builds.
- Check both direct and transitive dependencies against known CVEs; transitive dependencies carry the same risk and are far more often overlooked.
- Establish an update cadence — dependency review that only happens when a scanner flags something Critical means everything else quietly ages.
- Confirm dependencies are sourced from official registries, and that a typo-squatted package name hasn't been introduced by mistake.
The OSV (Open Source Vulnerabilities) schema is the closest thing to a common format most dependency scanners already speak — cross-referencing a lockfile against it produces records shaped like this:
{
"id": "GHSA-example-0000",
"summary": "Prototype pollution in example-pkg before 2.1.4",
"affected": [{ "package": { "name": "example-pkg", "ecosystem": "npm" },
"ranges": [{ "type": "SEMVER",
"events": [{ "introduced": "0" },
{ "fixed": "2.1.4" }] }] }],
"severity": [{ "type": "CVSS_V3", "score": "7.5" }]
}Unmaintained or vulnerable third-party components map to CWE-1104 (Use of Unmaintained Third-Party Components) — worth citing explicitly in a report, since it's a different weakness than any specific CVE and captures the ongoing-maintenance risk even for a dependency with no currently known vulnerability.
5Secret Detection
Secret detection covers more ground than most teams initially assume. Beyond obvious API keys and tokens, it should include:
- Private key material (RSA, EC, PGP) committed anywhere in the tree, not just in an obviously-named file.
.envfiles and infrastructure-as-code state files (.tfstate), which frequently contain live credentials in plaintext.- Git history, not just the current HEAD — a secret deleted in a later commit is still readable by anyone who checks out the earlier one.
- A mix of pattern-based detection (known key formats) and entropy-based detection (high-randomness strings that don't match a known pattern), since relying on one alone misses the other's blind spot.
Pattern-based detection matches a known credential shape directly — an AWS access key always starts with a recognizable prefix, for example:
# AWS access key ID
(A3T[A-Z0-9]|AKIA|AGPA|AIDA|AROA|AIPA|ANPA|ANVA|ASIA)[A-Z0-9]{16}
# Generic high-confidence assignment (key/token/secret = "...")
(?i)(api[_-]?key|secret|token)\s*[:=]\s*"[a-zA-Z0-9_-]{20,}"Entropy-based detection catches what pattern matching can't name in advance: a string that doesn't match any known credential format but is random enough that it's unlikely to be anything else. Shannon entropy over a string's character distribution is the standard measure — a string built from a small, predictable character set (like an English sentence) scores low, while a string that looks like uniformly random bytes scores high:
entropy(s) = -sum( p(c) * log2(p(c)) ) for each unique character c in s
"password123" -> ~3.1 bits/char (low -- predictable, English-like)
"kP9x!mQ2vZ7wR4tY" -> ~3.9 bits/char (high -- looks random)
# A common threshold: flag base64-charset strings scoring above
# ~4.5 bits/char as high-confidence secret candidates.Neither technique alone is sufficient. Pattern matching misses anything that doesn't match a known format; entropy scoring flags plenty of non-secrets that simply happen to look random (a hash, a UUID, a minified bundle identifier). Running both and cross- referencing the results is what keeps the false-positive and false-negative rates manageable — see the limitations section below.
Two CWE identifiers cover most of what secret detection is looking for:
| CWE | Covers |
|---|---|
CWE-798 | Use of Hard-coded Credentials — passwords, API keys, tokens embedded directly in source |
CWE-321 | Use of Hard-coded Cryptographic Key — signing keys, encryption keys committed to the repository |
6Repository Ownership
A repository without a clear owner is a repository nobody is responsible for reviewing, patching, or archiving when it's no longer needed. Two concrete checks:
- A
CODEOWNERSfile (or equivalent) mapping directories or services to the team or person responsible for reviewing changes to them. - An explicit answer for every repository to "who gets paged if this breaks" — an orphaned repository with production traffic and no owner is a specific, findable risk category, not just an organizational untidiness.
7What to Automate vs. What Needs a Person
| Check | Automatable | Why |
|---|---|---|
| Secret detection | ✓ Fully | Pattern + entropy scanning is deterministic |
| Dependency CVEs | ✓ Fully | Version match against a public vulnerability database |
| Branch protection settings | ✓ Fully | Directly queryable from the platform API |
| SAST pattern matching | ✓ Mostly | Heuristic — flags candidates, still needs review |
| Access review (who should have access) | — No | Requires knowing current team structure and need |
| Repository ownership | — No | An organizational fact, not a code property |
| Accepted-risk justification | — No | Requires a business decision, not a scan result |
8Triaging a Detected Secret
Finding a candidate secret is the start of a triage process, not the end of one. The questions worth asking in order:
| Question | If yes | If no |
|---|---|---|
| Is it a real credential (not a test fixture or example)? | Continue triage | Mark false positive, document why |
| Is it still valid / not already rotated? | Treat as live exposure — rotate immediately | Lower urgency, but still remove from history |
| Does it grant access beyond this repository? | Notify every system it authenticates to, not just this repo's owner | Scope the incident to this repository |
| Is it only in history, not current HEAD? | History rewrite or key rotation both needed — deleting the file alone doesn't remove it | Standard rotate-and-remove flow |
9False Positives and False Negatives
Secret detection's failure modes run in both directions, and each requires a different response:
- False positive — test fixtures. A unit test file containing
AWS_KEY = "AKIAIOSFODNN7EXAMPLE"matches the pattern exactly, because it's deliberately shaped like a real key. The fix isn't to weaken the pattern — it's to allowlist known example/placeholder values or exclude test fixture paths explicitly, so real findings in the same file aren't suppressed along with it. - False negative — split or constructed secrets. A key stored as
"AKIA" + "IOSFODNN7EXAMPLE"or assembled from two config values at runtime won't match a single-string pattern at all. This is why entropy scanning and pattern matching are complementary rather than redundant — but a sufficiently determined split still defeats both. - False negative — encoded secrets. A base64- or hex-encoded credential doesn't match a plaintext pattern and often doesn't score as high-entropy in its encoded form either, depending on the encoding. Decoding candidate strings before scoring closes some of this gap, at the cost of more false positives to review.
10How This Maps to Strata
The automatable checks above — secrets, sensitive files, SAST, and dependency CVEs — are the four passes Strata runs on every repository scan. See Repository Security Scanner for the full technical breakdown, or CI/CD Security Scanning to run the same checks automatically on every push or pull request.
This article supports Repository Security Scanner — see the product page for how Strata applies these ideas directly.
Visit Repository Security Scanner →