[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"public-config":3,"i-flag:ph-4x3":14,"i-flag:us-4x3":19,"i-flag:de-4x3":21,"i-flag:hk-4x3":23,"i-tabler:lifebuoy":25,"i-tabler:timeline":28,"i-tabler:apps":30,"i-tabler:package":32,"i-tabler:circle-half-2":34,"i-tabler:sun":36,"i-tabler:moon":38,"i-tabler:menu-2":40,"i-tabler:user":42,"i-tabler:notebook":44,"i-simple-icons:forgejo":46,"i-tabler:shield-lock":48,"i-tabler:tools":50,"i-tabler:calendar":52,"i-tabler:x":54,"$f32jemn58eeblm":56,"i-tabler:arrow-left":120,"i-tabler:tag":122},{"matomo":4,"booking":7,"site":9,"blog":11,"newsletter":12},{"enabled":5,"siteId":6},true,"7",{"enabled":5,"url":8},"https:\u002F\u002Fcal.com\u002Fjdbungart",{"url":10},"https:\u002F\u002Fjdneer.com",{"enabled":5},{"enabled":13},false,{"left":15,"top":15,"width":16,"height":17,"rotate":15,"vFlip":13,"hFlip":13,"body":18},0,640,480,"\u003Cpath fill=\"#0038a8\" d=\"M0 0h640v240H0z\"\u002F>\u003Cpath fill=\"#ce1126\" d=\"M0 240h640v240H0z\"\u002F>\u003Cpath fill=\"#fff\" d=\"M415.7 240L0 480V0\"\u002F>\u003Cpath fill=\"#fcd116\" d=\"M26.7 42.4L41 55l16.6-9.2l-7.4 17.5l14 13l-19-1.6l-8.1 17.2l-4.3-18.5L14 71l16.3-10zm323.8 172.3l.4 19l18 6.3l-18 6.2l-.4 19l-11.5-15.1l-18.2 5.5l10.8-15.6l-10.8-15.6l18.2 5.5zM37.2 388.1l8 17.2l19-1.6l-13.9 13l7.4 17.5l-16.6-9.1l-14.4 12.4l3.6-18.7L14 409l18.9-2.4zm114.2-249l-6.2 6.2l3.1 47l-3 .3l-5.7-42.9l-5.1 5l7.6 38.4a48 48 0 0 0-17.2 7.1l-21.7-32.4H96l26.4 34.3l-2.4 2l-31.1-35.5h-8.8v8.8l35.4 31l-2 2.5l-34.3-26.3v7.1l32.5 21.7q-5.2 7.8-7.1 17.2L66.3 223l-5.1 5l42.9 5.7q-.3 1.6-.3 3.1l-47-3l-6.2 6.2l6.2 6.2l47-3.1l.3 3.1l-42.9 5.7l5 5l38.4-7.6a48 48 0 0 0 7.1 17.2l-32.5 21.7v7.2l34.3-26.3l2 2.4l-35.4 31v8.8H89l31-35.4l2.5 2L96 312.2h7.2l21.7-32.5q7.8 5.2 17.2 7.1l-7.6 38.4l5 5l5.7-42.9q1.5.3 3.1.3l-3 47l6.1 6.2l6.3-6.2l-3.1-47l3-.3l5.7 43l5.1-5.1l-7.6-38.4a48 48 0 0 0 17.2-7.1l21.7 32.5h7.2l-26.4-34.3l2.4-2l31.1 35.4h8.8v-8.8l-35.4-31l2-2.4l34.3 26.3v-7.2l-32.5-21.7q5.2-7.8 7.1-17.2l38.3 7.6l5.1-5l-42.9-5.7q.3-1.5.3-3.1l47 3l6.2-6.1l-6.2-6.2l-47 3l-.3-3l42.9-5.7l-5-5l-38.4 7.5a48 48 0 0 0-7.1-17.2l32.5-21.7v-7.1l-34.3 26.3l-2-2.4l35.4-31v-8.9H214l-31 35.5l-2.5-2l26.4-34.3h-7.2L178 200.2q-7.8-5.2-17.2-7.1l7.6-38.3l-5-5l-5.7 42.8l-3.1-.3l3-47z\"\u002F>",{"left":15,"top":15,"width":16,"height":17,"rotate":15,"vFlip":13,"hFlip":13,"body":20},"\u003Cpath fill=\"#bd3d44\" d=\"M0 0h640v480H0\"\u002F>\u003Cpath stroke=\"#fff\" stroke-width=\"37\" d=\"M0 55.3h640M0 129h640M0 203h640M0 277h640M0 351h640M0 425h640\"\u002F>\u003Cpath fill=\"#192f5d\" d=\"M0 0h364.8v258.5H0\"\u002F>\u003Cmarker id=\"SVGEq3dreKJ\" markerHeight=\"30\" markerWidth=\"30\">\u003Cpath fill=\"#fff\" d=\"m14 0l9 27L0 10h28L5 27z\"\u002F>\u003C\u002Fmarker>\u003Cpath fill=\"none\" marker-mid=\"url(#SVGEq3dreKJ)\" d=\"m0 0l16 11h61h61h61h61h60L47 37h61h61h60h61L16 63h61h61h61h61h60L47 89h61h61h60h61L16 115h61h61h61h61h60L47 141h61h61h60h61L16 166h61h61h61h61h60L47 192h61h61h60h61L16 218h61h61h61h61h60z\"\u002F>",{"left":15,"top":15,"width":16,"height":17,"rotate":15,"vFlip":13,"hFlip":13,"body":22},"\u003Cpath fill=\"#fc0\" d=\"M0 320h640v160H0z\"\u002F>\u003Cpath fill=\"#000001\" d=\"M0 0h640v160H0z\"\u002F>\u003Cpath fill=\"red\" d=\"M0 160h640v160H0z\"\u002F>",{"left":15,"top":15,"width":16,"height":17,"rotate":15,"vFlip":13,"hFlip":13,"body":24},"\u003Cpath fill=\"#ec1b2e\" d=\"M0 0h640v480H0\"\u002F>\u003Cpath id=\"SVG5tfvBPpj\" fill=\"#fff\" d=\"M346.3 103.1C267 98 230.6 201.9 305.6 240.3c-26-22.4-20.6-55.3-10.1-72.4l1.9 1.1c-13.8 23.5-11.2 52.7 11.1 71c-12.7-12.3-9.5-39 12.1-48.9s23.6-39.3 16.4-49.1q-14.7-25.6 9.3-38.9M307.9 164l-4.7 7.4l-1.8-8.6l-8.6-2.3l7.8-4.3l-.6-8.9l6.5 6.1l8.3-3.3l-3.7 8.1l5.6 6.8z\"\u002F>\u003Cuse href=\"#SVG5tfvBPpj\" transform=\"rotate(72 312.5 243.5)\"\u002F>\u003Cuse href=\"#SVG5tfvBPpj\" transform=\"rotate(144 312.5 243.5)\"\u002F>\u003Cuse href=\"#SVG5tfvBPpj\" transform=\"rotate(216 312.5 243.5)\"\u002F>\u003Cuse href=\"#SVG5tfvBPpj\" transform=\"rotate(288 312.5 243.5)\"\u002F>",{"left":15,"top":15,"width":26,"height":26,"rotate":15,"vFlip":13,"hFlip":13,"body":27},24,"\u003Cg fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\">\u003Cpath d=\"M8 12a4 4 0 1 0 8 0a4 4 0 1 0-8 0\"\u002F>\u003Cpath d=\"M3 12a9 9 0 1 0 18 0a9 9 0 1 0-18 0m12 3l3.35 3.35M9 15l-3.35 3.35m0-12.7L9 9m9.35-3.35L15 9\"\u002F>\u003C\u002Fg>",{"left":15,"top":15,"width":26,"height":26,"rotate":15,"vFlip":13,"hFlip":13,"body":29},"\u003Cg fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\">\u003Cpath d=\"m4 16l6-7l5 5l5-6\"\u002F>\u003Cpath d=\"M14 14a1 1 0 1 0 2 0a1 1 0 1 0-2 0M9 9a1 1 0 1 0 2 0a1 1 0 1 0-2 0m-6 7a1 1 0 1 0 2 0a1 1 0 1 0-2 0m16-8a1 1 0 1 0 2 0a1 1 0 1 0-2 0\"\u002F>\u003C\u002Fg>",{"left":15,"top":15,"width":26,"height":26,"rotate":15,"vFlip":13,"hFlip":13,"body":31},"\u003Cpath fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M4 5a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v4a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1zm0 10a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v4a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1zm10 0a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v4a1 1 0 0 1-1 1h-4a1 1 0 0 1-1-1zm0-8h6m-3-3v6\"\u002F>",{"left":15,"top":15,"width":26,"height":26,"rotate":15,"vFlip":13,"hFlip":13,"body":33},"\u003Cpath fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"m12 3l8 4.5v9L12 21l-8-4.5v-9zm0 9l8-4.5M12 12v9m0-9L4 7.5m12-2.25l-8 4.5\"\u002F>",{"left":15,"top":15,"width":26,"height":26,"rotate":15,"vFlip":13,"hFlip":13,"body":35},"\u003Cpath fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M3 12a9 9 0 1 0 18 0a9 9 0 1 0-18 0m9-9v18m0-7l7-7m-7 12l8.5-8.5M12 9l4.5-4.5\"\u002F>",{"left":15,"top":15,"width":26,"height":26,"rotate":15,"vFlip":13,"hFlip":13,"body":37},"\u003Cpath fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M8 12a4 4 0 1 0 8 0a4 4 0 1 0-8 0m-5 0h1m8-9v1m8 8h1m-9 8v1M5.6 5.6l.7.7m12.1-.7l-.7.7m0 11.4l.7.7m-12.1-.7l-.7.7\"\u002F>",{"left":15,"top":15,"width":26,"height":26,"rotate":15,"vFlip":13,"hFlip":13,"body":39},"\u003Cpath fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M12 3h.393a7.5 7.5 0 0 0 7.92 12.446A9 9 0 1 1 12 2.992z\"\u002F>",{"left":15,"top":15,"width":26,"height":26,"rotate":15,"vFlip":13,"hFlip":13,"body":41},"\u003Cpath fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M4 6h16M4 12h16M4 18h16\"\u002F>",{"left":15,"top":15,"width":26,"height":26,"rotate":15,"vFlip":13,"hFlip":13,"body":43},"\u003Cpath fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M8 7a4 4 0 1 0 8 0a4 4 0 0 0-8 0M6 21v-2a4 4 0 0 1 4-4h4a4 4 0 0 1 4 4v2\"\u002F>",{"left":15,"top":15,"width":26,"height":26,"rotate":15,"vFlip":13,"hFlip":13,"body":45},"\u003Cpath fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M6 4h11a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H6a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1m3 0v18m4-14h2m-2 4h2\"\u002F>",{"left":15,"top":15,"width":26,"height":26,"rotate":15,"vFlip":13,"hFlip":13,"body":47},"\u003Cpath fill=\"currentColor\" d=\"M16.777 0a2.9 2.9 0 1 1-2.529 4.322H12.91a4.266 4.266 0 0 0-4.265 4.195v2.118a7.08 7.08 0 0 1 4.147-1.42l.118-.002h1.338a2.9 2.9 0 0 1 5.43 1.422a2.9 2.9 0 0 1-5.43 1.422H12.91a4.266 4.266 0 0 0-4.265 4.195v2.319A2.9 2.9 0 0 1 7.222 24A2.9 2.9 0 0 1 5.8 18.57V8.589a7.11 7.11 0 0 1 6.991-7.108l.118-.001h1.338A2.9 2.9 0 0 1 16.778 0M7.223 19.905a1.194 1.194 0 1 0 0 2.389a1.194 1.194 0 0 0 0-2.389m9.554-10.464a1.194 1.194 0 1 0 0 2.389a1.194 1.194 0 0 0 0-2.39m0-7.735a1.194 1.194 0 1 0 0 2.389a1.194 1.194 0 0 0 0-2.389\"\u002F>",{"left":15,"top":15,"width":26,"height":26,"rotate":15,"vFlip":13,"hFlip":13,"body":49},"\u003Cg fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\">\u003Cpath d=\"M12 3a12 12 0 0 0 8.5 3A12 12 0 0 1 12 21A12 12 0 0 1 3.5 6A12 12 0 0 0 12 3\"\u002F>\u003Cpath d=\"M11 11a1 1 0 1 0 2 0a1 1 0 1 0-2 0m1 1v2.5\"\u002F>\u003C\u002Fg>",{"left":15,"top":15,"width":26,"height":26,"rotate":15,"vFlip":13,"hFlip":13,"body":51},"\u003Cg fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\">\u003Cpath d=\"M3 21h4L20 8a1.5 1.5 0 0 0-4-4L3 17zM14.5 5.5l4 4\"\u002F>\u003Cpath d=\"M12 8L7 3L3 7l5 5M7 8L5.5 9.5M16 12l5 5l-4 4l-5-5m4 1l-1.5 1.5\"\u002F>\u003C\u002Fg>",{"left":15,"top":15,"width":26,"height":26,"rotate":15,"vFlip":13,"hFlip":13,"body":53},"\u003Cpath fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M4 7a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2zm12-4v4M8 3v4m-4 4h16m-9 4h1m0 0v3\"\u002F>",{"left":15,"top":15,"width":26,"height":26,"rotate":15,"vFlip":13,"hFlip":13,"body":55},"\u003Cpath fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M18 6L6 18M6 6l12 12\"\u002F>",{"slug":57,"title":58,"excerpt":59,"image":60,"publishedAt":61,"topics":62,"readMinutes":69,"html":70,"metaTitle":58,"metaDescription":71,"shareImage":72,"updatedAt":61,"author":76,"related":78},"package-cooldowns-github-package-firewall-npm-pnpm-bun-pip-uv-poetry","Package Cooldowns Compared: GitHub Package Firewall vs. What npm, pnpm, Yarn, Bun, pip, uv and Poetry Already Do","GitHub now offers an install-time firewall for npm, free in preview and metered later. Most package managers already ship a free cooldown. Here is what each tool does, the copy-paste config, and where cooldowns stop helping.","\u002Fcontent\u002Fimages\u002F2026\u002F10\u002Fpkg-cooldowns-conveyor-alberto-rodriguez-unsplash.jpg","2026-10-26T14:00:00.000+08:00",[63,66],{"key":64,"label":65},"guides","Guides",{"key":67,"label":68},"security","Security",10,"\u003Cp>On October 20, GitHub put a new product into public preview: \u003Ca href=\"https:\u002F\u002Fgithub.blog\u002Fchangelog\u002F2026-10-20-github-package-firewall-in-public-preview\u002F?ref=ghost.jdneer.com\">GitHub package firewall\u003C\u002Fa>. It sits between your machines and the npm registry and refuses to hand over package versions that break a policy you set. Coding agents and CI runners are the obvious target, since they install whatever the lockfile or the model asks for, with nobody watching.\u003C\u002Fp>\u003Cp>The headline feature is a cooldown: do not install anything published in the last N days. That idea is not new. Most package managers have shipped it since late 2025, for free. So I went through the vendor docs and changelogs for GitHub, npm, pnpm, Yarn, Bun, pip, uv and Poetry to see what each one actually does, and what you can turn on today.\u003C\u002Fp>\u003Ch2 id=\"why-cooldowns-work-at-all\">Why cooldowns work at all\u003C\u002Fh2>\u003Cp>Most malicious releases of popular packages are caught within hours. PyPI's \u003Ca href=\"https:\u002F\u002Fblog.pypi.org\u002Fposts\u002F2026-04-02-incident-report-litellm-telnyx-supply-chain-attack\u002F?ref=ghost.jdneer.com\">incident report on LiteLLM and telnyx\u003C\u002Fa> gives real numbers. The malicious LiteLLM versions were live for 2 hours 32 minutes from upload to quarantine, and were still downloaded over 119,000 times in that window. telnyx was live for 3 hours 42 minutes. On npm, the \u003Ca href=\"https:\u002F\u002Fwww.asyncapi.com\u002Fblog\u002Fmiasma-supply-chain-attck?ref=ghost.jdneer.com\">AsyncAPI maintainers\u003C\u002Fa> say the trojanized versions of their packages in July were available for about 4 hours 20 minutes.\u003C\u002Fp>\u003Cp>A one-day delay would have skipped all three. That is the whole pitch: let the scanners and the registry do their job before your build pulls the new version.\u003C\u002Fp>\u003Ch2 id=\"what-github-package-firewall-is\">What GitHub package firewall is\u003C\u002Fh2>\u003Cul>\u003Cli>\u003Cstrong>What it checks.\u003C\u002Fstrong> Per the \u003Ca href=\"https:\u002F\u002Fdocs.github.com\u002Fen\u002Fcode-security\u002Fhow-tos\u002Fsecure-your-supply-chain\u002Fsecure-your-dependencies\u002Fprotecting-endpoints-with-package-firewall?ref=ghost.jdneer.com\">setup docs\u003C\u002Fa>, enterprise admins set five controls: block known malware from the GitHub Advisory Database (on by default), block versions with vulnerability advisories above a severity you choose (default Critical only), block attestation downgrades where earlier versions had SLSA attestations and the new one does not (on), block versions that newly add \u003Ccode>preinstall\u003C\u002Fcode> or \u003Ccode>postinstall\u003C\u002Fcode> scripts (on), and a minimum package age of 1 to 365 days (default 1 day; 0 turns it off).\u003C\u002Fli>\u003Cli>\u003Cstrong>Where it works.\u003C\u002Fstrong> npm only, for now. You point npm at an enterprise-specific registry URL, or put it behind JFrog Artifactory (marked experimental). Every request needs a token from a member of the enterprise.\u003C\u002Fli>\u003Cli>\u003Cstrong>Who can use it.\u003C\u002Fstrong> GitHub Enterprise Cloud customers, including data residency.\u003C\u002Fli>\u003Cli>\u003Cstrong>What it costs.\u003C\u002Fstrong> Free during the preview. GitHub says that at general availability it will be billed on metered usage, similar to Actions. No price has been published yet.\u003C\u002Fli>\u003Cli>\u003Cstrong>When it blocks something you need.\u003C\u002Fstrong> The npm output includes a link to request an exception. Enterprise admins or enterprise security managers approve it, optionally for every version of that package.\u003C\u002Fli>\u003C\u002Ful>\u003Cpre>\u003Ccode class=\"language-ini\"># user-level ~\u002F.npmrc, replace ENTERPRISE with your enterprise slug\nregistry=https:\u002F\u002Fpackage-firewall.githubapp.com\u002Fnpm\u002Fenterprises\u002FENTERPRISE\n\u002F\u002Fpackage-firewall.githubapp.com\u002Fnpm\u002Fenterprises\u002FENTERPRISE\u002F:_authToken=${GITHUB_TOKEN}\u003C\u002Fcode>\u003C\u002Fpre>\u003Cp>The interesting part is not the cooldown. It is the attestation and install-script checks, which compare a new version against the package's own history. That is a signal a client-side setting cannot easily see.\u003C\u002Fp>\u003Ch2 id=\"the-comparison\">The comparison\u003C\u002Fh2>\u003Cp>Here is where every tool stands as of today, from each vendor's own documentation. Scroll sideways on a phone.\u003C\u002Fp>\n\n\u003Cdiv class=\"kg-card kg-width-full\">\u003Ctable style=\"width:100%\">\u003Cthead>\u003Ctr>\u003Cth style=\"text-align:left\">Tool\u003C\u002Fth>\u003Cth style=\"text-align:left\">Cooldown setting\u003C\u002Fth>\u003Cth style=\"text-align:left\">Unit\u003C\u002Fth>\u003Cth style=\"text-align:left\">Default\u003C\u002Fth>\u003Cth style=\"text-align:left\">Per-package exemption\u003C\u002Fth>\u003Cth style=\"text-align:left\">Since\u003C\u002Fth>\u003Cth style=\"text-align:left\">Other install-time checks\u003C\u002Fth>\u003C\u002Ftr>\u003C\u002Fthead>\u003Ctbody>\u003Ctr>\u003Ctd>GitHub Package Firewall\u003C\u002Ftd>\u003Ctd>Minimum package age (enterprise policy)\u003C\u002Ftd>\u003Ctd>Days, 1 to 365 (0 turns it off)\u003C\u002Ftd>\u003Ctd>1 day\u003C\u002Ftd>\u003Ctd>Bypass requests, approved by enterprise admins or enterprise security managers\u003C\u002Ftd>\u003Ctd>Public preview, Oct 20, 2026\u003C\u002Ftd>\u003Ctd>Blocks known malware (on), vulnerability advisories (Critical only by default), attestation downgrades (on), newly added install scripts (on). npm only. GitHub Enterprise Cloud only.\u003C\u002Ftd>\u003C\u002Ftr>\u003Ctr>\u003Ctd>npm CLI\u003C\u002Ftd>\u003Ctd>\u003Ccode>min-release-age\u003C\u002Fcode>\u003C\u002Ftd>\u003Ctd>Days\u003C\u002Ftd>\u003Ctd>Off\u003C\u002Ftd>\u003Ctd>\u003Ccode>min-release-age-exclude\u003C\u002Fcode> (11.17.0)\u003C\u002Ftd>\u003Ctd>11.10.0\u003C\u002Ftd>\u003Ctd>v12 (Jul 8, 2026): dependency install scripts blocked unless allowed in \u003Ccode>allowScripts\u003C\u002Fcode>; \u003Ccode>allow-git\u003C\u002Fcode> and \u003Ccode>allow-remote\u003C\u002Fcode> default to \u003Ccode>none\u003C\u002Fcode>.\u003C\u002Ftd>\u003C\u002Ftr>\u003Ctr>\u003Ctd>pnpm\u003C\u002Ftd>\u003Ctd>\u003Ccode>minimumReleaseAge\u003C\u002Fcode>\u003C\u002Ftd>\u003Ctd>Minutes\u003C\u002Ftd>\u003Ctd>1440 (1 day) since v11\u003C\u002Ftd>\u003Ctd>\u003Ccode>minimumReleaseAgeExclude\u003C\u002Fcode>\u003C\u002Ftd>\u003Ctd>10.16.0\u003C\u002Ftd>\u003Ctd>Dependency build scripts off since v10 (\u003Ccode>allowBuilds\u003C\u002Fcode> since 10.26.0), \u003Ccode>trustPolicy: no-downgrade\u003C\u002Fcode>, \u003Ccode>blockExoticSubdeps\u003C\u002Fcode> (on by default). Re-checks lockfile entries since 11.3.0.\u003C\u002Ftd>\u003C\u002Ftr>\u003Ctr>\u003Ctd>Yarn (Berry)\u003C\u002Ftd>\u003Ctd>\u003Ccode>npmMinimalAgeGate\u003C\u002Fcode>\u003C\u002Ftd>\u003Ctd>Duration string like \u003Ccode>\"3d\"\u003C\u002Fcode> or \u003Ccode>\"1w\"\u003C\u002Fcode>\u003C\u002Ftd>\u003Ctd>1 day since 4.15.0\u003C\u002Ftd>\u003Ctd>\u003Ccode>npmPreapprovedPackages\u003C\u002Fcode> (names or globs)\u003C\u002Ftd>\u003Ctd>4.10.0\u003C\u002Ftd>\u003Ctd>Third-party \u003Ccode>postinstall\u003C\u002Fcode> scripts off by default (\u003Ccode>enableScripts\u003C\u002Fcode>); \u003Ccode>enableHardenedMode\u003C\u002Fcode> checks the lockfile against the registry, on automatically for public-repo PRs on GitHub.\u003C\u002Ftd>\u003C\u002Ftr>\u003Ctr>\u003Ctd>Bun\u003C\u002Ftd>\u003Ctd>\u003Ccode>minimumReleaseAge\u003C\u002Fcode>\u003C\u002Ftd>\u003Ctd>Seconds (units like \u003Ccode>3d\u003C\u002Fcode> also accepted)\u003C\u002Ftd>\u003Ctd>Off\u003C\u002Ftd>\u003Ctd>\u003Ccode>minimumReleaseAgeExcludes\u003C\u002Fcode>\u003C\u002Ftd>\u003Ctd>1.3\u003C\u002Ftd>\u003Ctd>Lifecycle scripts run only for a built-in list of popular packages unless you set \u003Ccode>trustedDependencies\u003C\u002Fcode>, which replaces that list (\u003Ccode>[]\u003C\u002Fcode> turns it off); pluggable \u003Ccode>install.security.scanner\u003C\u002Fcode>.\u003C\u002Ftd>\u003C\u002Ftr>\u003Ctr>\u003Ctd>pip\u003C\u002Ftd>\u003Ctd>\u003Ccode>--uploaded-prior-to\u003C\u002Fcode>\u003C\u002Ftd>\u003Ctd>Date (26.0) or \u003Ccode>P&lt;n&gt;D\u003C\u002Fcode> days (26.1)\u003C\u002Ftd>\u003Ctd>Off\u003C\u002Ftd>\u003Ctd>None documented\u003C\u002Ftd>\u003Ctd>26.0 (absolute), 26.1 (relative)\u003C\u002Ftd>\u003Ctd>Hash checking (\u003Ccode>--require-hashes\u003C\u002Fcode>); 26.2 reads \u003Ccode>upload-time\u003C\u002Fcode> from \u003Ccode>pylock.toml\u003C\u002Fcode>.\u003C\u002Ftd>\u003C\u002Ftr>\u003Ctr>\u003Ctd>uv\u003C\u002Ftd>\u003Ctd>\u003Ccode>exclude-newer\u003C\u002Fcode>\u003C\u002Ftd>\u003Ctd>Timestamp, \"7 days\", or ISO 8601 like \u003Ccode>P7D\u003C\u002Fcode>\u003C\u002Ftd>\u003Ctd>Off\u003C\u002Ftd>\u003Ctd>\u003Ccode>exclude-newer-package\u003C\u002Fcode>\u003C\u002Ftd>\u003Ctd>Not stated in settings docs\u003C\u002Ftd>\u003Ctd>Optional OSV malware check during sync (\u003Ccode>[tool.uv.audit] malware-check\u003C\u002Fcode>, off by default).\u003C\u002Ftd>\u003C\u002Ftr>\u003Ctr>\u003Ctd>Poetry\u003C\u002Ftd>\u003Ctd>\u003Ccode>solver.min-release-age\u003C\u002Fcode>\u003C\u002Ftd>\u003Ctd>Days\u003C\u002Ftd>\u003Ctd>0 (off)\u003C\u002Ftd>\u003Ctd>\u003Ccode>solver.min-release-age-exclude\u003C\u002Fcode>, \u003Ccode>-exclude-source\u003C\u002Fcode>\u003C\u002Ftd>\u003Ctd>2.4.0\u003C\u002Ftd>\u003Ctd>Only enforced where the index exposes upload times.\u003C\u002Ftd>\u003C\u002Ftr>\u003Ctr>\u003Ctd>Socket Firewall Free\u003C\u002Ftd>\u003Ctd>None\u003C\u002Ftd>\u003Ctd>n\u002Fa\u003C\u002Ftd>\u003Ctd>n\u002Fa\u003C\u002Ftd>\u003Ctd>No allow-list in Free\u003C\u002Ftd>\u003Ctd>n\u002Fa\u003C\u002Ftd>\u003Ctd>Free wrapper (\u003Ccode>sfw npm install\u003C\u002Fcode>). Blocks confirmed malware for npm, yarn, pnpm, pip, uv and cargo. AI-flagged packages only warn; unknown or unscanned versions pass.\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003C\u002Fdiv>\n\n\u003Ch2 id=\"copy-paste-config-tool-by-tool\">Copy-paste config, tool by tool\u003C\u002Fh2>\u003Cp>I use 3 days in most examples. The PyPI team uses 3 days in its own guidance, uv's docs use examples like 7 days, and pnpm ships with 1. Pick a number your team can live with, and exempt your own packages so internal releases are not stuck for days.\u003C\u002Fp>\u003Ch3 id=\"npm\">npm\u003C\u002Fh3>\u003Cp>\u003Ca href=\"https:\u002F\u002Fdocs.npmjs.com\u002Fcli\u002Fv12\u002Fusing-npm\u002Fconfig?ref=ghost.jdneer.com\">\u003Ccode>min-release-age\u003C\u002Fcode>\u003C\u002Fa> arrived in npm 11.10.0 and is set in days. The exclude list, \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fnpm\u002Fcli\u002Fblob\u002Frelease\u002Fv11\u002FCHANGELOG.md?ref=ghost.jdneer.com\">\u003Ccode>min-release-age-exclude\u003C\u002Fcode>\u003C\u002Fa>, arrived in 11.17.0 and takes names or globs like \u003Ccode>@your-scope\u002F*\u003C\u002Fcode>. \u003Ccode>--allow-git\u003C\u002Fcode> has been around since early 2026 (11.9.0 per the \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fnpm\u002Fcli\u002Fblob\u002Frelease\u002Fv11\u002FCHANGELOG.md?ref=ghost.jdneer.com\">CLI changelog\u003C\u002Fa>). The other three, \u003Ccode>--allow-file\u003C\u002Fcode>, \u003Ccode>--allow-remote\u003C\u002Fcode> and \u003Ccode>--allow-directory\u003C\u002Fcode>, need \u003Ca href=\"https:\u002F\u002Fgithub.blog\u002Fchangelog\u002F2026-05-22-staged-publishing-and-new-install-time-controls-for-npm\u002F?ref=ghost.jdneer.com\">11.15.0 or newer\u003C\u002Fa>: they first appear in 11.14.0, with config support in 11.15.0.\u003C\u002Fp>\u003Cpre>\u003Ccode class=\"language-ini\"># ~\u002F.npmrc or project .npmrc (npm 11.17.0 or newer for the exclude list)\nmin-release-age=3\nmin-release-age-exclude[]=@your-scope\u002F*\n# Already the default on npm 12; set explicitly on npm 11.15.0+\nallow-git=none\nallow-remote=none\u003C\u002Fcode>\u003C\u002Fpre>\u003Cp>The bigger change is \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fnpm\u002Fcli\u002Freleases\u002Ftag\u002Fv12.0.0?ref=ghost.jdneer.com\">npm 12\u003C\u002Fa>, released July 8. It \u003Ca href=\"https:\u002F\u002Fgithub.blog\u002Fchangelog\u002F2026-07-08-npm-install-time-security-and-gat-bypass2fa-deprecation\u002F?ref=ghost.jdneer.com\">blocks dependency lifecycle scripts by default\u003C\u002Fa> (\u003Ccode>preinstall\u003C\u002Fcode>, \u003Ccode>install\u003C\u002Fcode>, \u003Ccode>postinstall\u003C\u002Fcode> and implicit \u003Ccode>node-gyp\u003C\u002Fcode> builds) unless the package is listed in \u003Ccode>allowScripts\u003C\u002Fcode> in your \u003Ccode>package.json\u003C\u002Fcode>. It also stops resolving git and remote tarball dependencies unless you allow them. npm 11.16.0 and newer \u003Ca href=\"https:\u002F\u002Fgithub.blog\u002Fchangelog\u002F2026-06-09-upcoming-breaking-changes-for-npm-v12\u002F?ref=ghost.jdneer.com\">show warnings\u003C\u002Fa> for all of this, so you can prepare first.\u003C\u002Fp>\u003Cpre>\u003Ccode class=\"language-bash\"># npm 12: see which dependencies want to run install scripts\nnpm approve-scripts --allow-scripts-pending\n# approve the ones you trust; entries are pinned to the installed version by default\n# (--no-allow-scripts-pin writes name-only entries), then commit package.json\nnpm approve-scripts esbuild sharp\n# explicitly block the rest\nnpm deny-scripts some-package\nnpm rebuild\u003C\u002Fcode>\u003C\u002Fpre>\u003Cp>When \u003Ccode>min-release-age\u003C\u002Fcode> blocks \u003Ccode>npm audit fix\u003C\u002Fcode> from installing a fresh patch, npm keeps the vulnerable version, warns, and exits non-zero. Add the package to the exclude list to take the fix.\u003C\u002Fp>\u003Ch3 id=\"pnpm\">pnpm\u003C\u002Fh3>\u003Cp>pnpm and Yarn both ship a one-day cooldown by default. In pnpm, since v11, \u003Ca href=\"https:\u002F\u002Fpnpm.io\u002Fsettings\u002Fdependency-resolution?ref=ghost.jdneer.com\">\u003Ccode>minimumReleaseAge\u003C\u002Fcode>\u003C\u002Fa> defaults to 1440 minutes (1 day). One catch: the built-in default is non-strict, so if no version in your range is old enough, pnpm quietly falls back to a newer one. Set the value yourself and strict mode turns on. pnpm also stopped running dependency build scripts in v10. Today you approve them with \u003Ca href=\"https:\u002F\u002Fpnpm.io\u002Fsettings\u002Fbuild?ref=ghost.jdneer.com\">\u003Ccode>allowBuilds\u003C\u002Fcode>\u003C\u002Fa>, added in 10.26.0; earlier releases used \u003Ccode>onlyBuiltDependencies\u003C\u002Fcode>, which \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fpnpm\u002Fpnpm\u002Freleases\u002Ftag\u002Fv11.0.0?ref=ghost.jdneer.com\">v11 removed\u003C\u002Fa>. pnpm's \u003Ca href=\"https:\u002F\u002Fpnpm.io\u002Fsupply-chain-security?ref=ghost.jdneer.com\">\u003Ccode>trustPolicy: no-downgrade\u003C\u002Fcode>\u003C\u002Fa> refuses a version whose trust evidence dropped compared to earlier releases.\u003C\u002Fp>\u003Cpre>\u003Ccode class=\"language-yaml\"># pnpm-workspace.yaml\nminimumReleaseAge: 4320        # 3 days, in minutes\nminimumReleaseAgeExclude:\n  - '@your-scope\u002F*'\ntrustPolicy: no-downgrade\nallowBuilds:\n  esbuild: true\u003C\u002Fcode>\u003C\u002Fpre>\u003Ch3 id=\"yarn\">Yarn\u003C\u002Fh3>\u003Cp>Yarn (the modern 4.x line) added \u003Ca href=\"https:\u002F\u002Fyarnpkg.com\u002Fconfiguration\u002Fyarnrc?ref=ghost.jdneer.com\">\u003Ccode>npmMinimalAgeGate\u003C\u002Fcode>\u003C\u002Fa> in \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fyarnpkg\u002Fberry\u002Freleases\u002Ftag\u002F%40yarnpkg%2Fcli%2F4.10.0?ref=ghost.jdneer.com\">4.10.0\u003C\u002Fa>, and \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fyarnpkg\u002Fberry\u002Freleases\u002Ftag\u002F%40yarnpkg%2Fcli%2F4.15.0?ref=ghost.jdneer.com\">4.15.0\u003C\u002Fa> (May 19, 2026) made \u003Ccode>1d\u003C\u002Fcode> the default. Exemptions go in \u003Ccode>npmPreapprovedPackages\u003C\u002Fcode>, which takes package names or globs. Per the same docs, Yarn does not run third-party \u003Ccode>postinstall\u003C\u002Fcode> scripts unless you turn on \u003Ccode>enableScripts\u003C\u002Fcode>.\u003C\u002Fp>\u003Cpre>\u003Ccode class=\"language-yaml\"># .yarnrc.yml\nnpmMinimalAgeGate: \"3d\"\nnpmPreapprovedPackages:\n  - \"@your-scope\u002F*\"\u003C\u002Fcode>\u003C\u002Fpre>\u003Ch3 id=\"bun\">Bun\u003C\u002Fh3>\u003Cp>\u003Ca href=\"https:\u002F\u002Fbun.com\u002Fdocs\u002Fruntime\u002Fbunfig?ref=ghost.jdneer.com\">\u003Ccode>minimumReleaseAge\u003C\u002Fcode>\u003C\u002Fa> arrived in \u003Ca href=\"https:\u002F\u002Fbun.com\u002Fblog\u002Fbun-v1.3?ref=ghost.jdneer.com\">Bun 1.3\u003C\u002Fa> and is in seconds, though the \u003Ca href=\"https:\u002F\u002Fbun.com\u002Fdocs\u002Fpm\u002Fcli\u002Finstall?ref=ghost.jdneer.com\">install docs\u003C\u002Fa> say it also takes units like \u003Ccode>3d\u003C\u002Fcode> or \u003Ccode>2 weeks\u003C\u002Fcode>. Exemptions can name a whole package or specific vetted versions. Bun also does not run arbitrary dependency lifecycle scripts by default. Without \u003Ca href=\"https:\u002F\u002Fbun.com\u002Fdocs\u002Fpm\u002Flifecycle?ref=ghost.jdneer.com\">\u003Ccode>trustedDependencies\u003C\u002Fcode>\u003C\u002Fa>, it still runs them for a built-in list of popular packages. Setting the field replaces that list, and \u003Ccode>trustedDependencies: []\u003C\u002Fcode> turns it off entirely.\u003C\u002Fp>\u003Cpre>\u003Ccode class=\"language-toml\"># bunfig.toml (or ~\u002F.bunfig.toml for every project)\n[install]\nminimumReleaseAge = 259200     # 3 days, in seconds\nminimumReleaseAgeExcludes = [\"@your-scope\u002Fshared\"]\u003C\u002Fcode>\u003C\u002Fpre>\u003Ch3 id=\"pip\">pip\u003C\u002Fh3>\u003Cp>\u003Ca href=\"https:\u002F\u002Fpip.pypa.io\u002Fen\u002Fstable\u002Fnews\u002F?ref=ghost.jdneer.com\">pip 26.0\u003C\u002Fa> added \u003Ccode>--uploaded-prior-to\u003C\u002Fcode> with an absolute date. pip 26.1 lets it take a duration in days, like \u003Ccode>P3D\u003C\u002Fcode>, which is what makes it usable as a standing cooldown in config. I could not find a per-package exemption in \u003Ca href=\"https:\u002F\u002Fpip.pypa.io\u002Fen\u002Fstable\u002Fuser_guide\u002F?ref=ghost.jdneer.com\">pip's docs\u003C\u002Fa>; you override the whole setting with \u003Ccode>P0D\u003C\u002Fcode> on the command line.\u003C\u002Fp>\u003Cpre>\u003Ccode class=\"language-ini\"># ~\u002F.config\u002Fpip\u002Fpip.conf  (pip 26.1 or newer)\n[install]\nuploaded-prior-to = P3D\u003C\u002Fcode>\u003C\u002Fpre>\u003Cpre>\u003Ccode class=\"language-bash\"># one-off, or override the config for an urgent fix\npython -m pip install --uploaded-prior-to=P3D requests\npython -m pip install --uploaded-prior-to=P0D somepackage==1.2.3\u003C\u002Fcode>\u003C\u002Fpre>\u003Ch3 id=\"uv\">uv\u003C\u002Fh3>\u003Cp>\u003Ca href=\"https:\u002F\u002Fdocs.astral.sh\u002Fuv\u002Freference\u002Fsettings\u002F?ref=ghost.jdneer.com\">\u003Ccode>exclude-newer\u003C\u002Fcode>\u003C\u002Fa> takes an RFC 3339 timestamp, a friendly duration like \u003Ccode>\"7 days\"\u003C\u002Fcode>, or an ISO 8601 duration like \u003Ccode>\"P7D\"\u003C\u002Fcode>. They mean the same thing, so the \u003Ccode>\"P3D\"\u003C\u002Fcode> example in PyPI's guidance and the \u003Ccode>\"7 days\"\u003C\u002Fcode> style in uv's docs are both valid. \u003Ccode>exclude-newer-package\u003C\u002Fcode> gives per-package dates, or \u003Ccode>false\u003C\u002Fcode> to exempt a package entirely.\u003C\u002Fp>\u003Cpre>\u003Ccode class=\"language-toml\"># pyproject.toml  (or drop the [tool.uv] header in ~\u002F.config\u002Fuv\u002Fuv.toml)\n[tool.uv]\nexclude-newer = \"7 days\"\nexclude-newer-package = { my-internal-lib = false }\u003C\u002Fcode>\u003C\u002Fpre>\u003Ch3 id=\"poetry\">Poetry\u003C\u002Fh3>\u003Cp>\u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fpython-poetry\u002Fpoetry\u002Fblob\u002Fmain\u002FCHANGELOG.md?ref=ghost.jdneer.com\">Poetry 2.4.0\u003C\u002Fa> added \u003Ca href=\"https:\u002F\u002Fpython-poetry.org\u002Fdocs\u002Fconfiguration\u002F?ref=ghost.jdneer.com\">\u003Ccode>solver.min-release-age\u003C\u002Fcode>\u003C\u002Fa> in days, plus exclusions by package or by whole index. Poetry notes it can only filter on indexes that publish upload times.\u003C\u002Fp>\u003Cpre>\u003Ccode class=\"language-bash\">poetry config solver.min-release-age 3\npoetry config solver.min-release-age-exclude \"my-internal-lib\"\u003C\u002Fcode>\u003C\u002Fpre>\u003Ch3 id=\"socket-firewall-free\">Socket Firewall Free\u003C\u002Fh3>\u003Cp>If you want malware blocking without GitHub Enterprise, \u003Ca href=\"https:\u002F\u002Fdocs.socket.dev\u002Fdocs\u002Fsocket-firewall-free?ref=ghost.jdneer.com\">Socket Firewall Free\u003C\u002Fa> is a free wrapper you put in front of the install command. No API key, no config. It blocks only malware confirmed by human review; AI-flagged packages get a warning. Telemetry is always on in the free version, it only works with public registries, and it uses the PolyForm Shield license. It does not block unknown or unscanned versions either, so it does not replace a cooldown. And it cannot block what is already in your local cache, so clear the cache once when you start.\u003C\u002Fp>\u003Cpre>\u003Ccode class=\"language-bash\">npm i -g sfw\nsfw npm ci\nsfw uv pip install -r requirements.txt\u003C\u002Fcode>\u003C\u002Fpre>\u003Ch2 id=\"what-the-registries-do-on-their-side\">What the registries do on their side\u003C\u002Fh2>\u003Cul>\u003Cli>\u003Cstrong>npm scans at publish time.\u003C\u002Fstrong> Since \u003Ca href=\"https:\u002F\u002Fgithub.blog\u002Fchangelog\u002F2026-07-28-npm-publish-time-malware-scanning-and-dual-use-metadata\u002F?ref=ghost.jdneer.com\">July 28\u003C\u002Fa>, new npm packages are scanned before they become installable, usually taking about five minutes. A version can be published, held for review, or blocked.\u003C\u002Fli>\u003Cli>\u003Cstrong>npm staged publishing.\u003C\u002Fstrong> Since \u003Ca href=\"https:\u002F\u002Fgithub.blog\u002Fchangelog\u002F2026-05-22-staged-publishing-and-new-install-time-controls-for-npm\u002F?ref=ghost.jdneer.com\">May 22\u003C\u002Fa> (CLI 11.15.0), maintainers can make CI publish to a queue that a human must approve with a 2FA check. That protects you only if the maintainers of your dependencies turn it on.\u003C\u002Fli>\u003Cli>\u003Cstrong>PyPI quarantine is both manual and automatic.\u003C\u002Fstrong> PyPI admins have been able to \u003Ca href=\"https:\u002F\u002Fblog.pypi.org\u002Fposts\u002F2024-12-30-quarantine\u002F?ref=ghost.jdneer.com\">quarantine a project\u003C\u002Fa> since 2024, which hides it from installers and can be reversed. In the \u003Ca href=\"https:\u002F\u002Fblog.pypi.org\u002Fposts\u002F2026-04-02-incident-report-litellm-telnyx-supply-chain-attack\u002F?ref=ghost.jdneer.com\">April incident report\u003C\u002Fa>, PyPI says the telnyx response was taken \"autonomously\" because reports from its pool of trusted reporters \"add weight to any given report, triggering an automated quarantine feature.\" So: admins still quarantine by hand, and enough trusted reports can now trigger it automatically.\u003C\u002Fli>\u003Cli>\u003Cstrong>Attestations prove origin, not safety.\u003C\u002Fstrong> PyPI's own \u003Ca href=\"https:\u002F\u002Fdocs.pypi.org\u002Fattestations\u002Fsecurity-model\u002F?ref=ghost.jdneer.com\">security model\u003C\u002Fa> puts it plainly: \"An attestation will tell you where a PyPI package came from, but not whether you should trust it.\"\u003C\u002Fli>\u003C\u002Ful>\u003Ch2 id=\"where-cooldowns-stop-helping\">Where cooldowns stop helping\u003C\u002Fh2>\u003Cul>\u003Cli>\u003Cstrong>Patient attackers.\u003C\u002Fstrong> A cooldown is a delay, not a scan. If nobody notices a malicious version within your window, you install it anyway. Seven days buys more than one, but nothing is guaranteed.\u003C\u002Fli>\u003Cli>\u003Cstrong>Properly signed malware.\u003C\u002Fstrong> In the AsyncAPI attack, the attacker pushed code to release branches and let the project's own pipeline publish it, so the packages carried \u003Ca href=\"https:\u002F\u002Fwww.asyncapi.com\u002Fblog\u002Fmiasma-supply-chain-attck?ref=ghost.jdneer.com\">valid OIDC trusted publishing provenance\u003C\u002Fa>. GitHub's attestation downgrade check and pnpm's \u003Ccode>trustPolicy\u003C\u002Fcode> look for missing or weaker trust evidence. Neither would catch that case. And the payload \u003Ca href=\"https:\u002F\u002Fwww.microsoft.com\u002Fen-us\u002Fsecurity\u002Fblog\u002F2026\u002F07\u002F15\u002Funpacking-asyncapi-npm-supply-chain-compromise-import-time-payload-delivery\u002F?ref=ghost.jdneer.com\">ran when the library was loaded\u003C\u002Fa>, not from an install script, so blocking install scripts would not have stopped it either.\u003C\u002Fli>\u003Cli>\u003Cstrong>Lockfiles.\u003C\u002Fstrong> Cooldowns filter new resolution. \u003Ca href=\"https:\u002F\u002Fbun.com\u002Fdocs\u002Fpm\u002Fcli\u002Finstall?ref=ghost.jdneer.com\">Bun's docs\u003C\u002Fa> say it outright: existing packages in \u003Ccode>bun.lock\u003C\u002Fcode> stay unchanged. pnpm is stricter: \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fpnpm\u002Fpnpm\u002Freleases\u002Ftag\u002Fv11.3.0?ref=ghost.jdneer.com\">since 11.3.0\u003C\u002Fa> it re-applies \u003Ccode>minimumReleaseAge\u003C\u002Fcode> and \u003Ccode>trustPolicy\u003C\u002Fcode> to lockfile entries unless you set \u003Ccode>trustLockfile: true\u003C\u002Fcode>. npm's docs do not say it re-checks an existing \u003Ccode>package-lock.json\u003C\u002Fcode>, so I would not count on it. Review lockfile changes in pull requests, especially from outside contributors.\u003C\u002Fli>\u003Cli>\u003Cstrong>Urgent security fixes.\u003C\u002Fstrong> A cooldown also delays the patch. PyPI notes that Dependabot and Renovate skip cooldowns for security updates by default; for everything else, use the exemption lists.\u003C\u002Fli>\u003Cli>\u003Cstrong>Everything that is not npm or PyPI.\u003C\u002Fstrong> GitHub's firewall covers npm only today, and none of these settings touch container images, GitHub Actions, or install scripts piped from curl. Pin base images by digest (Docker's \u003Ca href=\"https:\u002F\u002Fdocs.docker.com\u002Fbuild\u002Fbuilding\u002Fbest-practices\u002F?ref=ghost.jdneer.com\">best practices\u003C\u002Fa> explain why), and pin Actions by commit SHA rather than tag.\u003C\u002Fli>\u003C\u002Ful>\u003Cpre>\u003Ccode class=\"language-dockerfile\"># Dockerfile: pin the base image by digest, not just by tag\nFROM node:24-slim@sha256:&lt;digest-you-verified&gt;\u003C\u002Fcode>\u003C\u002Fpre>\u003Ch2 id=\"what-i-would-do\">What I would do\u003C\u002Fh2>\u003Col>\u003Cli>\u003Cstrong>Turn on a cooldown everywhere today.\u003C\u002Fstrong> It costs nothing. 3 days is a sensible start, with your own packages exempted. Put it in the user-level config on developer machines, and in the project config so CI gets it too.\u003C\u002Fli>\u003Cli>\u003Cstrong>Get to npm 12, or stay on pnpm 10 or newer, Yarn 4, or Bun.\u003C\u002Fstrong> Blocking dependency install scripts by default closes the most common way these attacks run code.\u003C\u002Fli>\u003Cli>\u003Cstrong>Commit lockfiles and install from them\u003C\u002Fstrong> (\u003Ccode>npm ci\u003C\u002Fcode>, \u003Ccode>pnpm install --frozen-lockfile\u003C\u002Fcode>, \u003Ccode>yarn install --immutable\u003C\u002Fcode>, \u003Ccode>bun install --frozen-lockfile\u003C\u002Fcode>, \u003Ccode>uv sync --locked\u003C\u002Fcode>).\u003C\u002Fli>\u003Cli>\u003Cstrong>If you are already on GitHub Enterprise Cloud,\u003C\u002Fstrong> try Package Firewall on CI and agent runners while it is free. Its install-script and attestation checks add something a cooldown cannot.\u003C\u002Fli>\u003Cli>\u003Cstrong>If you are not,\u003C\u002Fstrong> Socket Firewall Free covers malware blocking for npm, pnpm, yarn, pip, uv and cargo. Commercial gateways like \u003Ca href=\"https:\u002F\u002Fhelp.sonatype.com\u002Fen\u002Frepository-firewall.html?ref=ghost.jdneer.com\">Sonatype Repository Firewall\u003C\u002Fa> cover far more ecosystems, including Docker, if you need one policy for everything.\u003C\u002Fli>\u003C\u002Fol>\u003Cp>Cooldowns are the cheapest supply-chain control I know. They will not stop a determined attacker, but a one-day delay would have skipped all three incidents above, and it takes one line of config.\u003C\u002Fp>\u003Cp>\u003Cem>Sources: \u003C\u002Fem>\u003Ca href=\"https:\u002F\u002Fgithub.blog\u002Fchangelog\u002F2026-10-20-github-package-firewall-in-public-preview\u002F?ref=ghost.jdneer.com\">\u003Cem>GitHub changelog\u003C\u002Fem>\u003C\u002Fa>\u003Cem>, \u003C\u002Fem>\u003Ca href=\"https:\u002F\u002Fdocs.github.com\u002Fen\u002Fcode-security\u002Fhow-tos\u002Fsecure-your-supply-chain\u002Fsecure-your-dependencies\u002Fprotecting-endpoints-with-package-firewall?ref=ghost.jdneer.com\">\u003Cem>GitHub package firewall docs\u003C\u002Fem>\u003C\u002Fa>\u003Cem>, \u003C\u002Fem>\u003Ca href=\"https:\u002F\u002Fgithub.blog\u002Fchangelog\u002F2026-05-22-staged-publishing-and-new-install-time-controls-for-npm\u002F?ref=ghost.jdneer.com\">\u003Cem>npm staged publishing\u003C\u002Fem>\u003C\u002Fa>\u003Cem>, \u003C\u002Fem>\u003Ca href=\"https:\u002F\u002Fgithub.blog\u002Fchangelog\u002F2026-06-09-upcoming-breaking-changes-for-npm-v12\u002F?ref=ghost.jdneer.com\">\u003Cem>npm v12 breaking changes\u003C\u002Fem>\u003C\u002Fa>\u003Cem>, \u003C\u002Fem>\u003Ca href=\"https:\u002F\u002Fgithub.blog\u002Fchangelog\u002F2026-07-08-npm-install-time-security-and-gat-bypass2fa-deprecation\u002F?ref=ghost.jdneer.com\">\u003Cem>npm v12 GA\u003C\u002Fem>\u003C\u002Fa>\u003Cem>, \u003C\u002Fem>\u003Ca href=\"https:\u002F\u002Fgithub.blog\u002Fchangelog\u002F2026-07-28-npm-publish-time-malware-scanning-and-dual-use-metadata\u002F?ref=ghost.jdneer.com\">\u003Cem>npm publish-time scanning\u003C\u002Fem>\u003C\u002Fa>\u003Cem>, \u003C\u002Fem>\u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fnpm\u002Fcli\u002Fblob\u002Frelease\u002Fv11\u002FCHANGELOG.md?ref=ghost.jdneer.com\">\u003Cem>npm CLI v11 changelog\u003C\u002Fem>\u003C\u002Fa>\u003Cem>, \u003C\u002Fem>\u003Ca href=\"https:\u002F\u002Fdocs.npmjs.com\u002Fcli\u002Fv12\u002Fusing-npm\u002Fconfig?ref=ghost.jdneer.com\">\u003Cem>npm config\u003C\u002Fem>\u003C\u002Fa>\u003Cem>, \u003C\u002Fem>\u003Ca href=\"https:\u002F\u002Fdocs.npmjs.com\u002Fcli\u002Fv12\u002Fcommands\u002Fnpm-approve-scripts\u002F?ref=ghost.jdneer.com\">\u003Cem>npm approve-scripts\u003C\u002Fem>\u003C\u002Fa>\u003Cem>, \u003C\u002Fem>\u003Ca href=\"https:\u002F\u002Fpnpm.io\u002Fsupply-chain-security?ref=ghost.jdneer.com\">\u003Cem>pnpm supply chain security\u003C\u002Fem>\u003C\u002Fa>\u003Cem>, \u003C\u002Fem>\u003Ca href=\"https:\u002F\u002Fpnpm.io\u002Fsettings\u002Fdependency-resolution?ref=ghost.jdneer.com\">\u003Cem>pnpm settings\u003C\u002Fem>\u003C\u002Fa>\u003Cem>, \u003C\u002Fem>\u003Ca href=\"https:\u002F\u002Fyarnpkg.com\u002Fconfiguration\u002Fyarnrc?ref=ghost.jdneer.com\">\u003Cem>Yarn settings\u003C\u002Fem>\u003C\u002Fa>\u003Cem>, \u003C\u002Fem>\u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fyarnpkg\u002Fberry\u002Freleases\u002Ftag\u002F%40yarnpkg%2Fcli%2F4.15.0?ref=ghost.jdneer.com\">\u003Cem>Yarn 4.15.0\u003C\u002Fem>\u003C\u002Fa>\u003Cem>, \u003C\u002Fem>\u003Ca href=\"https:\u002F\u002Fbun.com\u002Fdocs\u002Fpm\u002Fcli\u002Finstall?ref=ghost.jdneer.com\">\u003Cem>Bun install docs\u003C\u002Fem>\u003C\u002Fa>\u003Cem>, \u003C\u002Fem>\u003Ca href=\"https:\u002F\u002Fbun.com\u002Fdocs\u002Fpm\u002Flifecycle?ref=ghost.jdneer.com\">\u003Cem>Bun lifecycle scripts\u003C\u002Fem>\u003C\u002Fa>\u003Cem>, \u003C\u002Fem>\u003Ca href=\"https:\u002F\u002Fbun.com\u002Fblog\u002Fbun-v1.3?ref=ghost.jdneer.com\">\u003Cem>Bun 1.3\u003C\u002Fem>\u003C\u002Fa>\u003Cem>, \u003C\u002Fem>\u003Ca href=\"https:\u002F\u002Fpip.pypa.io\u002Fen\u002Fstable\u002Fnews\u002F?ref=ghost.jdneer.com\">\u003Cem>pip changelog\u003C\u002Fem>\u003C\u002Fa>\u003Cem>, \u003C\u002Fem>\u003Ca href=\"https:\u002F\u002Fpip.pypa.io\u002Fen\u002Fstable\u002Fuser_guide\u002F?ref=ghost.jdneer.com\">\u003Cem>pip user guide\u003C\u002Fem>\u003C\u002Fa>\u003Cem>, \u003C\u002Fem>\u003Ca href=\"https:\u002F\u002Fdocs.astral.sh\u002Fuv\u002Freference\u002Fsettings\u002F?ref=ghost.jdneer.com\">\u003Cem>uv settings\u003C\u002Fem>\u003C\u002Fa>\u003Cem>, \u003C\u002Fem>\u003Ca href=\"https:\u002F\u002Fpython-poetry.org\u002Fdocs\u002Fconfiguration\u002F?ref=ghost.jdneer.com\">\u003Cem>Poetry configuration\u003C\u002Fem>\u003C\u002Fa>\u003Cem>, \u003C\u002Fem>\u003Ca href=\"https:\u002F\u002Fblog.pypi.org\u002Fposts\u002F2026-04-02-incident-report-litellm-telnyx-supply-chain-attack\u002F?ref=ghost.jdneer.com\">\u003Cem>PyPI incident report\u003C\u002Fem>\u003C\u002Fa>\u003Cem>, \u003C\u002Fem>\u003Ca href=\"https:\u002F\u002Fblog.pypi.org\u002Fposts\u002F2024-12-30-quarantine\u002F?ref=ghost.jdneer.com\">\u003Cem>PyPI quarantine\u003C\u002Fem>\u003C\u002Fa>\u003Cem>, \u003C\u002Fem>\u003Ca href=\"https:\u002F\u002Fdocs.pypi.org\u002Fattestations\u002Fsecurity-model\u002F?ref=ghost.jdneer.com\">\u003Cem>PyPI attestation security model\u003C\u002Fem>\u003C\u002Fa>\u003Cem>, \u003C\u002Fem>\u003Ca href=\"https:\u002F\u002Fdocs.socket.dev\u002Fdocs\u002Fsocket-firewall-free?ref=ghost.jdneer.com\">\u003Cem>Socket Firewall Free\u003C\u002Fem>\u003C\u002Fa>\u003Cem>, \u003C\u002Fem>\u003Ca href=\"https:\u002F\u002Fwww.asyncapi.com\u002Fblog\u002Fmiasma-supply-chain-attck?ref=ghost.jdneer.com\">\u003Cem>AsyncAPI incident report\u003C\u002Fem>\u003C\u002Fa>\u003Cem>, \u003C\u002Fem>\u003Ca href=\"https:\u002F\u002Fwww.microsoft.com\u002Fen-us\u002Fsecurity\u002Fblog\u002F2026\u002F07\u002F15\u002Funpacking-asyncapi-npm-supply-chain-compromise-import-time-payload-delivery\u002F?ref=ghost.jdneer.com\">\u003Cem>Microsoft on AsyncAPI\u003C\u002Fem>\u003C\u002Fa>\u003Cem>.\u003C\u002Fem>\u003C\u002Fp>","GitHub now offers an install-time firewall for npm, free in preview and metered later. Most package managers already ship a free cooldown. Here is what...",{"src":60,"height":73,"width":74,"type":75},1125,2000,"image\u002Fjpeg",{"name":77,"url":10,"owner":5},"JD Bungart",[79,91,102,111],{"slug":80,"title":81,"excerpt":82,"image":83,"publishedAt":84,"topics":85,"readMinutes":90},"http2shell-cve-2026-111497-brotli-decoder","HTTP2Shell (CVE-2026-111497): What the Brotli Decoder Bug Really Hits, and How to Patch","CVE-2026-111497 is an out-of-bounds write in Google's Brotli decoder, 1.0.3 through 1.2.0. Fixed in 1.0.10, 1.1.1 and 1.2.1, plus Node.js, Bun, Python, Envoy and .NET updates. You are exposed if you decode untrusted Brotli data. Not in CISA KEV.","\u002Fcontent\u002Fimages\u002F2026\u002F10\u002Fhttp2shell_cve_2026_111497_v2.png","2026-10-26T20:51:27.000+08:00",[86,89],{"key":87,"label":88},"advisories","Advisories",{"key":67,"label":68},1,{"slug":92,"title":93,"excerpt":94,"image":95,"publishedAt":96,"topics":97,"readMinutes":90},"tailscale-ssh-tailworm-mesh-vpn-agent-risk","Your Mesh VPN Agent Is Root on Every Box. Tailworm Is the Reminder.","A researcher showed one hacked Tailscale node reaching root on other servers through Tailscale SSH forwarding. No patch yet. Here is why mesh VPN agents deserve the same suspicion as any privileged daemon.","\u002Fcontent\u002Fimages\u002F2026\u002F10\u002Ftailworm-padlock-light-trails-flyd-unsplash.jpg","2026-10-26T12:00:00.000+08:00",[98,101],{"key":99,"label":100},"opinion","Opinion",{"key":67,"label":68},{"slug":103,"title":104,"excerpt":105,"image":106,"publishedAt":107,"topics":108,"readMinutes":90},"emudhra-ca-weak-keys-chain-of-trust","eMudhra Broke Almost Nothing, and Still Dented the Chain of Trust","Eight eMudhra intermediate CA keys turned out to be numbers under 256. Browsers blocked them within hours, almost no site broke, and eMudhra is leaving the TLS business. The service impact was tiny. The lesson for the chain of trust is not.","\u002Fcontent\u002Fimages\u002F2026\u002F10\u002Femudhra-keys-iolandino-baiano-unsplash.jpg","2026-10-25T16:00:00.000+08:00",[109,110],{"key":99,"label":100},{"key":67,"label":68},{"slug":112,"title":113,"excerpt":114,"image":115,"publishedAt":116,"topics":117,"readMinutes":90},"insider-threat-lab-poisoning-cases","The Threat Was Already Badged In: Insider Lessons From Two Lab Poisoning Cases","A Wyoming grad student is accused of poisoning an officemate, and the internet already called it espionage. The charge says assault. What the court record actually shows, and the access, procurement, and reporting controls that would have caught it sooner.","\u002Fcontent\u002Fimages\u002F2026\u002F10\u002Finsider-night-office-brunobd-unsplash.jpg","2026-10-25T14:00:00.000+08:00",[118,119],{"key":99,"label":100},{"key":67,"label":68},{"left":15,"top":15,"width":26,"height":26,"rotate":15,"vFlip":13,"hFlip":13,"body":121},"\u003Cpath fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M5 12h14M5 12l6 6m-6-6l6-6\"\u002F>",{"left":15,"top":15,"width":26,"height":26,"rotate":15,"vFlip":13,"hFlip":13,"body":123},"\u003Cg fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\">\u003Cpath d=\"M6.5 7.5a1 1 0 1 0 2 0a1 1 0 1 0-2 0\"\u002F>\u003Cpath d=\"M3 6v5.172a2 2 0 0 0 .586 1.414l7.71 7.71a2.41 2.41 0 0 0 3.408 0l5.592-5.592a2.41 2.41 0 0 0 0-3.408l-7.71-7.71A2 2 0 0 0 11.172 3H6a3 3 0 0 0-3 3\"\u002F>\u003C\u002Fg>"]