{"id":613,"date":"2026-11-14T15:31:54","date_gmt":"2026-11-14T15:31:54","guid":{"rendered":"https:\/\/kurtgrung.com\/blog\/?p=613"},"modified":"2026-11-14T15:36:49","modified_gmt":"2026-11-14T15:36:49","slug":"how-nullvoid-uses-github","status":"publish","type":"post","link":"https:\/\/kurtgrung.com\/blog\/how-nullvoid-uses-github\/","title":{"rendered":"How NullVoid uses GitHub"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">NullVoid is a static scanner for malicious code in npm packages and Node.js projects. GitHub data feeds it in three places: live vulnerability lookups during a scan, community signals about a package&#8217;s source repository, and the training labels for its machine-learning models.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">1. Live advisory lookups (GitHub Security Advisories)<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">For every declared dependency, NullVoid queries the <a href=\"https:\/\/github.com\/advisories\">GitHub Advisory Database<\/a> through the GraphQL API (<code>https:\/\/api.github.com\/graphql<\/code>). It asks for npm-ecosystem advisories affecting the package, then checks the installed version against each advisory&#8217;s <code>vulnerableVersionRange<\/code> so only advisories that actually apply are reported.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Each matching advisory becomes a finding carrying:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>the GHSA ID and permalink, plus any CVE identifiers<\/li>\n\n\n\n<li>GitHub&#8217;s severity (CRITICAL \/ HIGH \/ MODERATE \/ LOW) and CVSS score<\/li>\n\n\n\n<li>CWE classifications<\/li>\n\n\n\n<li>the first patched version, so the report can say what to upgrade to<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">GHSA is one of several IoC providers, alongside npm advisories, NVD and optionally Snyk. Results are cached and combined with the other sources. It is enabled by default; set <code>GITHUB_TOKEN<\/code> (or <code>NULLVOID_GITHUB_TOKEN<\/code>) for higher rate limits. See <a href=\"https:\/\/github.com\/kurt-grung\/NullVoid\/blob\/main\/docs\/IOC_USAGE.md\">IOC_USAGE.md<\/a> for configuration.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Code: <a href=\"https:\/\/github.com\/kurt-grung\/NullVoid\/blob\/main\/ts\/src\/lib\/providers\/ghsa.ts\"><code>ts\/src\/lib\/providers\/ghsa.ts<\/code><\/a><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">2. Community signals from the source repository<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">When a package&#8217;s <code>repository<\/code> field points at GitHub, NullVoid reads the repository&#8217;s public metadata from the REST API (<code>GET \/repos\/{owner}\/{repo}<\/code>) and uses its star count as one input (30% weight, alongside npm downloads and dependents) to a popularity score. That score feeds the dependency-confusion and ML detectors, so a brand-new package with no real repository behind it is treated differently from an established, widely starred project.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Code: <a href=\"https:\/\/github.com\/kurt-grung\/NullVoid\/blob\/main\/ts\/src\/lib\/communityAnalysis.ts\"><code>ts\/src\/lib\/communityAnalysis.ts<\/code><\/a><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">3. Training data for the ML models<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">NullVoid&#8217;s dependency and behavioral models are retrained on a schedule by a GitHub Actions workflow (<a href=\"https:\/\/github.com\/kurt-grung\/NullVoid\/blob\/main\/.github\/workflows\/ml-model-retrain.yml\"><code>ml-model-retrain.yml<\/code><\/a>). The export scripts page through the REST advisories endpoint (<code>GET \/advisories?ecosystem=npm&amp;type=malware<\/code>, plus <code>type=reviewed<\/code>), skip withdrawn advisories, and collect the affected npm packages as labelled examples. GitHub&#8217;s curated malware advisories are what let the models learn what real npm supply-chain attacks look like, rather than relying on synthetic samples.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Code: <a href=\"https:\/\/github.com\/kurt-grung\/NullVoid\/blob\/main\/ml-model\/export-features.js\"><code>ml-model\/export-features.js<\/code><\/a>, <a href=\"https:\/\/github.com\/kurt-grung\/NullVoid\/blob\/main\/ml-model\/export-behavioral-features.js\"><code>ml-model\/export-behavioral-features.js<\/code><\/a><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Running NullVoid on GitHub<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">NullVoid also runs inside GitHub: it can emit SARIF, which GitHub code scanning picks up through <code>github\/codeql-action\/upload-sarif<\/code>, so findings show up in a repository&#8217;s Security tab and on pull requests. The workflow is in the <a href=\"https:\/\/github.com\/kurt-grung\/NullVoid#readme\">README<\/a> and <a href=\"https:\/\/github.com\/kurt-grung\/NullVoid\/blob\/main\/docs\/CI_INTEGRATION.md\">CI_INTEGRATION.md<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Support<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Questions or problems: <a href=\"https:\/\/github.com\/kurt-grung\/NullVoid\/issues\">open an issue<\/a> or email <a href=\"mailto:caterer.01boars@icloud.com\">caterer.01boars@icloud.com<\/a>. Report vulnerabilities as described in <a href=\"https:\/\/github.com\/kurt-grung\/NullVoid\/blob\/main\/SECURITY.md\">SECURITY.md<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>NullVoid is a static scanner for malicious code in npm packages and Node.js projects. GitHub data feeds it in three places: live vulnerability lookups during a scan, community signals about a package&#8217;s source repository, and the training labels for its machine-learning models. 1. Live advisory lookups (GitHub Security Advisories) For every declared dependency, NullVoid queries [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[8],"tags":[179],"class_list":["post-613","post","type-post","status-publish","format-standard","hentry","category-code","tag-nullvoid"],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/kurtgrung.com\/blog\/wp-json\/wp\/v2\/posts\/613","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/kurtgrung.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/kurtgrung.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/kurtgrung.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/kurtgrung.com\/blog\/wp-json\/wp\/v2\/comments?post=613"}],"version-history":[{"count":3,"href":"https:\/\/kurtgrung.com\/blog\/wp-json\/wp\/v2\/posts\/613\/revisions"}],"predecessor-version":[{"id":616,"href":"https:\/\/kurtgrung.com\/blog\/wp-json\/wp\/v2\/posts\/613\/revisions\/616"}],"wp:attachment":[{"href":"https:\/\/kurtgrung.com\/blog\/wp-json\/wp\/v2\/media?parent=613"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/kurtgrung.com\/blog\/wp-json\/wp\/v2\/categories?post=613"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/kurtgrung.com\/blog\/wp-json\/wp\/v2\/tags?post=613"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}