Skip to content

Code and Dependency Security

7 min readGit Security & DevSecOps · Code & Dependency Security

Two problems share a cluster here because they arrive through the same pull request, and are otherwise almost nothing alike.

Code you wrote can contain vulnerabilities you introduced, which you can fix by changing the code. Code you depend on can contain vulnerabilities somebody else introduced, in a package you may not have chosen, at a depth in the tree you have never looked at — and your remediation options are upgrade, replace, or accept.

Conflating them produces the two classic failures: treating a dependency advisory as an emergency when the vulnerable function is never called, and treating a static-analysis finding as noise when it is a real path from user input to a database.

Start with GitHub Code Scanning
Two analysis paths, one pull request

Source code splits into two branches. On the left, static analysis via code scanning and CodeQL. On the right, dependencies, analysed by the dependency graph, Dependabot and dependency review. Both converge on the pull request.

Source codeStatic analysisDependenciesCodeQL / SARIF toolsDependency graphDependabot / reviewPull request

The left branch asks “does this code do something dangerous with data it does not control?” The right branch asks “what is in here that I did not write, and is any of it known to be bad?”

Neither answers the other’s question, and neither replaces review.

Detection and remediation are separate systems

Section titled “Detection and remediation are separate systems”

The most useful framing for this cluster is that GitHub gives you four tools that are commonly described as one product, and they do genuinely different jobs.

ToolQuestion it answersTrigger
Code scanningDoes the source contain a pattern that represents a vulnerability?Push, pull request, schedule
Dependency graphWhat does this repository actually depend on, including transitively?Manifest and lock file changes
DependabotIs anything I already depend on known to be vulnerable, or out of date?Advisory publication, schedule
Dependency reviewDoes this pull request make dependency risk worse?Pull request

The fourth is the one teams most often skip, and it is the only one positioned to prevent rather than report. Dependabot tells you about a problem you already have. Dependency review tells you about a problem you are about to acquire, at the moment somebody can still decide not to.

Threat. A defect reaches production — either written into first-party code, or pulled in with a dependency — and is reachable by an attacker.

Attack surface. Every code path that handles input the attacker influences: request parameters, uploaded files, message queues, environment configuration. Plus the entire transitive dependency tree, plus the package resolution process itself, which decides which artifact a name resolves to.

Impact. Ranges from nothing (the vulnerable code path is unreachable in your usage) to complete compromise. That range is why blind patching by severity score is a poor strategy: severity describes the vulnerability, not your exposure to it.

Control. Static analysis on every pull request; a complete dependency inventory; automated updates with enough CI to make them safe to merge; and a review gate on dependency changes.

Verification. Introduce a known-vulnerable dependency in a scratch repository and confirm the pull request is annotated. Confirm a deliberately vulnerable code pattern is caught — and note which of your real patterns are not.

Worth stating plainly, because it is usually treated as an operational annoyance rather than a control failure.

A team with 400 open Dependabot pull requests has the same effective security posture as a team with Dependabot switched off, minus the time spent ignoring the tab. Detection that nobody acts on is not detection; it is a record that would have been useful.

Every tool in this cluster has noise controls, and using them is not a compromise:

  • Grouped updates turn twelve pull requests into one.
  • Cooldown periods let a fresh release settle before you take it.
  • Auto-triage rules dismiss classes of alert that do not apply, with a stated reason.
  • Severity thresholds on the dependency review action decide what blocks a merge.
  • Query suites control how aggressive code scanning is.

Tuning these is how the queue stays short enough that a real finding is visible in it.

  1. Lesson 1: 01. GitHub Code ScanningCode scanning as a platform — default and advanced setup, third-party SARIF tools, alert severity and lifecycle, pull request behaviour, merge gating through rulesets, and organisation rollout.Intermediate14 min read
  2. Lesson 2: 02. CodeQLCodeQL explained — databases, queries and query suites, supported languages and build modes, default and advanced setup, custom query packs, triage and the analysis's real limits.Advanced16 min read
  3. Lesson 3: 03. DependabotThe three Dependabot systems explained — alerts, security updates and version updates — with dependabot.yml configuration, grouping, cooldowns, auto-triage rules and noise control that works.Intermediate15 min read
  4. Lesson 4: 04. Dependency ReviewReviewing dependency changes at the pull request — what dependency review shows, the action's severity, scope, licence and package controls, OpenSSF Scorecard signals.Intermediate14 min read
  5. Lesson 5: 05. Dependency GraphThe inventory everything else reads from — how the dependency graph is built from manifests and lock files, direct versus transitive dependencies, the dependency submission API.Intermediate12 min read
  6. Lesson 6: 06. Vulnerability AlertsTurning advisories into decisions — severity versus exposure, reachability, the advisory database, dismissal reasons, auto-triage rules.Intermediate13 min read
  7. Lesson 7: 07. Copilot AutofixAI-suggested fixes for code scanning alerts — what Autofix covers, how GitHub validates suggestions before showing them, its documented failure modes.Intermediate13 min read
  8. Lesson 8: 08. Security CampaignsManaging security debt at scale — what a campaign is, alert selection and templates, deadlines and ownership, developer notification, Autofix integration, tracking, and the limits worth knowing.Intermediate13 min read

Lessons 1 and 2 cover static analysis: the platform feature, then the engine underneath it. Lessons 3 to 6 cover the dependency side, deliberately starting with the graph — you cannot manage what you have not inventoried. Lesson 7 covers AI-suggested fixes with the scepticism they require. Lesson 8 covers remediating at organisational scale, which is a different problem from remediating one alert.

Remediation differs, and that is the point

Section titled “Remediation differs, and that is the point”

The reason to keep first-party and third-party findings mentally separate is that the fix looks nothing alike.

A first-party finding is a defect in code you control. You can read the surrounding function, decide whether the reported path is genuinely reachable, and change it. The main risks are misjudging reachability and fixing the symptom — sanitising one call site while leaving the pattern in place elsewhere.

A third-party finding is a defect in code you do not control, and your options are constrained:

  • Upgrade to a fixed version, which is usually right and occasionally impossible because the fix landed in a major release with breaking changes.
  • Pin a transitive dependency through your package manager’s override mechanism, which fixes the resolved version without waiting for your direct dependency to update. This works, and it silently puts you on a combination the maintainer never tested.
  • Replace the dependency, which is a project, not a patch.
  • Accept the risk with a documented reason — legitimate when the vulnerable code path is genuinely unreachable in your usage, and dangerous as a habit because “unreachable” changes when your code does.

Notice that three of those four are decisions about your system that no scanner can make for you. The tooling’s job is to put a correct, complete question in front of the person who can answer it.

They are frequently described as alternatives. They are layers, and each depends on the one before.

  1. The dependency graph is the substrate. It parses manifests and lock files to determine what you actually depend on, direct and transitive. Nothing else in the dependency chain works without it.
  2. Dependabot alerts join that inventory against the GitHub Advisory Database. New advisory, or new dependency — either can produce an alert.
  3. Dependabot updates act on those alerts, and separately keep versions current so that when an urgent upgrade is needed you are not also absorbing two years of drift.
  4. Dependency review watches the change, in the pull request, before any of the above becomes your problem.

On the static analysis side the layering is shallower but the same shape: code scanning is the platform — alerts, pull request annotations, SARIF ingestion, merge gating — and CodeQL is the analysis engine GitHub ships for it. Third-party analysers that emit SARIF plug into the same platform, which means the triage workflow stays identical whichever engine produced the finding.

Enabling everything on day one. Turning on code scanning across an organisation produces a backlog measured in thousands, and a backlog nobody can triage teaches everyone to ignore the interface. Start with the default query suite on active repositories.

Treating the dependency graph as complete. It reads manifests and lock files. Vendored code, system packages installed in a Dockerfile and dynamically fetched dependencies are invisible to it unless submitted explicitly.

Merging Dependabot pull requests without CI. An automated dependency bump with no test coverage is an automated production change with no test coverage.

Dismissing alerts without a reason. The dismissal reason is the only record of the judgement. Without it, the next person re-does the analysis or, more often, re-opens the argument.

Code and dependency security is about what is in the software. The final cluster is about the software’s journey: whether the artifact you deploy was built from the source you reviewed, by the system you expect, and whether anyone downstream can check that independently.

Next cluster — Software Supply Chain Security