The govulncheck approach (tracing actual code paths to verify vulnerable functions are called) should be the default for every ecosystem, not just Go.
The fundamental problem with Dependabot is that it treats dependency management as a security problem when it's actually a maintenance problem. A vulnerability in a function you never call is not a security issue — it's noise. But Dependabot can't distinguish the two because it operates at the version level, not the call graph level.
For Python projects I've found pip-audit with the --desc flag more useful than Dependabot. It's still version-based, but at least it doesn't create PRs that break your CI at 3am. The real solution is better static analysis that understands reachability, but until that exists for every ecosystem, turning off the noisy tools and doing manual quarterly audits might actually be more secure in practice — because you'll actually read the results instead of auto-merging them.
Part of the problem is that customers will scan your code with these tools and they won't accept "we never call that function" as an answer (and maybe that's rational if they can't verify that that's true). This is where actual security starts to really diverge from the practices we've developed in the name of security.
Would be neat if the call graph could be asserted easily.. As you could not only validate what vulnerabilities you are / aren't exposed to, but also choose to blacklist some API calls as a form of mitigation. Ensuring you don't accidentally start using something that's proven unsafe.
It’s easier to just update the package and not have to worry.
https://bandit.readthedocs.io/en/latest/ can do that for python.
but then if you could assert the call graph (easily, or even provably correctly), then why not just cull the unused code that led to vulnerability in the first place?
With a statically compiled language it is usually culled through dead-code elimination (DCE), and with static linking you don’t ship entire libraries.
The technology to cull code can work for dynamic languages too, even tho it does get difficult sometimes (google closure compiler[1] does dead code elimination for js, for example). It's just that most dynamic language users don't make the attempt (and you end up with this dependabot giving you thousands of false positives due to the deep dependency tree).
[1]https://github.com/google/closure-compiler
There is the VEX justification Vulnerable_code_not_in_execute_path. But it's an application-level assertion. I don't think there's a standardized mechanism that can describe this at the component level, from which the application-level assertion could be synthesized. Standardized vulnerability metadata is per component, not per component-to-component relationship. So it's just easier to fix vulnerability.
But I don't quite understand what Dependabot is doing for Go specifically. The vulnerability goes away without source code changes if the dependency is updated from version 1.1.0 to 1.1.1. So anyone building the software (producing an application binary) could just do that, and the intermediate packages would not have to change at all. But it doesn't seem like the standard Go toolchain automates this.
If you never call it why is it there?
It's in the library you're using, and you're not using all of it. I've had that exact situation: a dependency was vulnerable in a very specific set of circumstances which never occurred in my usage, but it got flagged by Dependabot and I received a couple of unnecessary issues.