[{"data":1,"prerenderedAt":550},["ShallowReactive",2],{"docs-navigation:en":3,"content-page:docs:kMc0hP5NG2L4mTg_kb9K8sCsjqu_SBBUQ5a0YsaqRMk":72,"content-page-surround:docs:kMc0hP5NG2L4mTg_kb9K8sCsjqu_SBBUQ5a0YsaqRMk":547},[4,12,18,24,30,36,42,48,54,60,66],{"title":5,"id":6,"canonicalKey":7,"locale":8,"draft":9,"icon":10,"path":11},"Overview","content:docs:1.overview.md","1","en",false,"lucide:compass","\u002Fdocs\u002Foverview",{"title":13,"id":14,"canonicalKey":15,"locale":8,"draft":9,"icon":16,"path":17},"Checklist","content:docs:2.checklist.md","2","lucide:list-checks","\u002Fdocs\u002Fchecklist",{"title":19,"id":20,"canonicalKey":21,"locale":8,"draft":9,"icon":22,"path":23},"Set up a repository","content:docs:3.set-up-a-repository.md","3","lucide:folder-plus","\u002Fdocs\u002Fset-up-a-repository",{"title":25,"id":26,"canonicalKey":27,"locale":8,"draft":9,"icon":28,"path":29},"Migrate a repository","content:docs:4.migrate-a-repository.md","4","lucide:arrow-right-left","\u002Fdocs\u002Fmigrate-a-repository",{"title":31,"id":32,"canonicalKey":33,"locale":8,"draft":9,"icon":34,"path":35},"Releasing","content:docs:5.releasing.md","5","lucide:rocket","\u002Fdocs\u002Freleasing",{"title":37,"id":38,"canonicalKey":39,"locale":8,"draft":9,"icon":40,"path":41},"Write library docs","content:docs:6.writing-docs.md","6","lucide:book-open","\u002Fdocs\u002Fwriting-docs",{"title":43,"id":44,"canonicalKey":45,"locale":8,"draft":9,"icon":46,"path":47},"Dependencies","content:docs:7.dependencies.md","7","lucide:package-search","\u002Fdocs\u002Fdependencies",{"title":49,"id":50,"canonicalKey":51,"locale":8,"draft":9,"icon":52,"path":53},"Security reports and incidents","content:docs:8.security.md","8","lucide:shield-alert","\u002Fdocs\u002Fsecurity",{"title":55,"id":56,"canonicalKey":57,"locale":8,"draft":9,"icon":58,"path":59},"Adding tooling","content:docs:9.adding-tooling.md","9","lucide:hammer","\u002Fdocs\u002Fadding-tooling",{"title":61,"id":62,"canonicalKey":63,"locale":8,"draft":9,"icon":64,"path":65},"Maintain the fleet","content:docs:10.maintain-the-fleet.md","10","lucide:calendar-check","\u002Fdocs\u002Fmaintain-the-fleet",{"title":67,"id":68,"canonicalKey":69,"locale":8,"draft":9,"icon":70,"path":71},"Troubleshooting","content:docs:11.troubleshooting.md","11","lucide:wrench","\u002Fdocs\u002Ftroubleshooting",{"title":49,"description":73,"icon":52,"redirectFrom":74,"body":77,"type":535,"id":50,"canonicalKey":51,"draft":9,"partial":9,"locale":8,"collection":536,"navigationFile":9,"file":537,"route":542,"resolution":544},"Handle a vulnerability report, and what to do when a credential, account or dependency may be compromised.",[75,76],"\u002Fdocs\u002Fstandards\u002Fsecurity-and-agents","\u002Fdocs\u002Fprocedures\u002Fcredential-incident",{"type":78,"children":79,"toc":525},"root",[80,89,173,179,185,205,236,241,247,252,362,374,380,428,434,452,507],{"type":81,"tag":82,"props":83,"children":85},"element","h2",{"id":84},"what-protects-a-release",[86],{"type":87,"value":88},"text","What protects a release",{"type":81,"tag":90,"props":91,"children":92},"ul",{},[93,107,125,130,135,152,163,168],{"type":81,"tag":94,"props":95,"children":96},"li",{},[97,99,105],{"type":87,"value":98},"Publishing needs the maintainer's approval in the ",{"type":81,"tag":100,"props":101,"children":102},"code",{},[103],{"type":87,"value":104},"npm",{"type":87,"value":106}," environment.",{"type":81,"tag":94,"props":108,"children":109},{},[110,112,117,119,123],{"type":87,"value":111},"npm accepts publishes only from ",{"type":81,"tag":100,"props":113,"children":114},{},[115],{"type":87,"value":116},"release.yml",{"type":87,"value":118}," in the ",{"type":81,"tag":100,"props":120,"children":121},{},[122],{"type":87,"value":104},{"type":87,"value":124}," environment of the\nright repository (trusted publishing), and the packages disallow tokens. There\nis no npm token anywhere to steal.",{"type":81,"tag":94,"props":126,"children":127},{},[128],{"type":87,"value":129},"The publish job does not check out or install anything, so a compromised\ndependency cannot run code while publish rights exist.",{"type":81,"tag":94,"props":131,"children":132},{},[133],{"type":87,"value":134},"Every version after the first carries npm provenance that links it to the\nexact commit and workflow run.",{"type":81,"tag":94,"props":136,"children":137},{},[138,143,145,150],{"type":81,"tag":100,"props":139,"children":140},{},[141],{"type":87,"value":142},"main",{"type":87,"value":144}," accepts only pull requests with a green ",{"type":81,"tag":100,"props":146,"children":147},{},[148],{"type":87,"value":149},"ci",{"type":87,"value":151}," check from GitHub\nActions.",{"type":81,"tag":94,"props":153,"children":154},{},[155,157,161],{"type":87,"value":156},"Release tags cannot be moved or deleted, and ",{"type":81,"tag":100,"props":158,"children":159},{},[160],{"type":87,"value":116},{"type":87,"value":162}," tags only the\ncommit it packed.",{"type":81,"tag":94,"props":164,"children":165},{},[166],{"type":87,"value":167},"The release run lists every change to the release workflows and scripts since\nthe last release, next to the approval.",{"type":81,"tag":94,"props":169,"children":170},{},[171],{"type":87,"value":172},"The maintainer's GitHub and npm recovery codes are stored in a password\nmanager. With one maintainer, they are the only way back into the accounts.",{"type":81,"tag":174,"props":175,"children":176},"p",{},[177],{"type":87,"value":178},"Keep these properties. Everything else in this page is what to do when\nsomething goes wrong.",{"type":81,"tag":82,"props":180,"children":182},{"id":181},"a-vulnerability-report-arrives",[183],{"type":87,"value":184},"A vulnerability report arrives",{"type":81,"tag":174,"props":186,"children":187},{},[188,190,196,198,203],{"type":87,"value":189},"Reports come through GitHub private vulnerability reporting (",{"type":81,"tag":191,"props":192,"children":193},"strong",{},[194],{"type":87,"value":195},"Security →\nAdvisories",{"type":87,"value":197},"). ",{"type":81,"tag":100,"props":199,"children":200},{},[201],{"type":87,"value":202},".github\u002FSECURITY.md",{"type":87,"value":204}," in each repository says so and lists the supported\nversions: the latest major.",{"type":81,"tag":206,"props":207,"children":208},"ol",{},[209,214,219,231],{"type":81,"tag":94,"props":210,"children":211},{},[212],{"type":87,"value":213},"Acknowledge within a week. Say whether you can reproduce it.",{"type":81,"tag":94,"props":215,"children":216},{},[217],{"type":87,"value":218},"Work in the draft advisory. For a serious issue, use its temporary private\nfork for the fix, so the fix is not public before the release. For a minor\nissue, a normal pull request with a neutral title is fine.",{"type":81,"tag":94,"props":220,"children":221},{},[222,224,229],{"type":87,"value":223},"Release the fix through the normal flow with a ",{"type":81,"tag":100,"props":225,"children":226},{},[227],{"type":87,"value":228},"patch",{"type":87,"value":230}," changeset.",{"type":81,"tag":94,"props":232,"children":233},{},[234],{"type":87,"value":235},"Publish the advisory with the fixed version and request a CVE from the\nadvisory page. Credit the reporter if they want it.",{"type":81,"tag":174,"props":237,"children":238},{},[239],{"type":87,"value":240},"Do not discuss an unfixed vulnerability in a public issue. If someone opens\none, copy the details into a draft advisory and delete the issue.",{"type":81,"tag":82,"props":242,"children":244},{"id":243},"a-credential-may-be-exposed",[245],{"type":87,"value":246},"A credential may be exposed",{"type":81,"tag":174,"props":248,"children":249},{},[250],{"type":87,"value":251},"Assume it is real. Do the provider step first; removing the value from Git\ndoes not revoke anything.",{"type":81,"tag":253,"props":254,"children":255},"table",{},[256,275],{"type":81,"tag":257,"props":258,"children":259},"thead",{},[260],{"type":81,"tag":261,"props":262,"children":263},"tr",{},[264,270],{"type":81,"tag":265,"props":266,"children":267},"th",{},[268],{"type":87,"value":269},"What",{"type":81,"tag":265,"props":271,"children":272},{},[273],{"type":87,"value":274},"Do now",{"type":81,"tag":276,"props":277,"children":278},"tbody",{},[279,309,329,342],{"type":81,"tag":261,"props":280,"children":281},{},[282,288],{"type":81,"tag":283,"props":284,"children":285},"td",{},[286],{"type":87,"value":287},"npm account",{"type":81,"tag":283,"props":289,"children":290},{},[291,293,300,302,307],{"type":87,"value":292},"Change the password, check 2FA, revoke all tokens on ",{"type":81,"tag":294,"props":295,"children":297},"a",{"href":296},"https:\u002F\u002Fwww.npmjs.com",[298],{"type":87,"value":299},"npmjs.com",{"type":87,"value":301}," (",{"type":81,"tag":191,"props":303,"children":304},{},[305],{"type":87,"value":306},"Access Tokens",{"type":87,"value":308},"), review recent versions of every package.",{"type":81,"tag":261,"props":310,"children":311},{},[312,317],{"type":81,"tag":283,"props":313,"children":314},{},[315],{"type":87,"value":316},"GitHub account or personal access token",{"type":81,"tag":283,"props":318,"children":319},{},[320,322,327],{"type":87,"value":321},"Revoke the token, sign out other sessions, check SSH keys, OAuth apps and ",{"type":81,"tag":191,"props":323,"children":324},{},[325],{"type":87,"value":326},"Settings → Security log",{"type":87,"value":328},".",{"type":81,"tag":261,"props":330,"children":331},{},[332,337],{"type":81,"tag":283,"props":333,"children":334},{},[335],{"type":87,"value":336},"A secret committed to a repository",{"type":81,"tag":283,"props":338,"children":339},{},[340],{"type":87,"value":341},"Rotate it at the provider. Then remove it from the code. Push protection should have blocked it; find out why not.",{"type":81,"tag":261,"props":343,"children":344},{},[345,350],{"type":81,"tag":283,"props":346,"children":347},{},[348],{"type":87,"value":349},"Vercel account or token",{"type":81,"tag":283,"props":351,"children":352},{},[353,355,360],{"type":87,"value":354},"Revoke tokens in Vercel ",{"type":81,"tag":191,"props":356,"children":357},{},[358],{"type":87,"value":359},"Account → Tokens",{"type":87,"value":361},". CI does not need any Vercel token.",{"type":81,"tag":174,"props":363,"children":364},{},[365,367,372],{"type":87,"value":366},"Then check what happened during the exposure window: npm versions published,\nGitHub releases and tags, workflow runs, changes to rulesets, environments and\nsecrets. The audit (",{"type":81,"tag":100,"props":368,"children":369},{},[370],{"type":87,"value":371},"pnpm audit:repos",{"type":87,"value":373},") shows the current settings quickly.",{"type":81,"tag":82,"props":375,"children":377},{"id":376},"a-malicious-version-was-published",[378],{"type":87,"value":379},"A malicious version was published",{"type":81,"tag":206,"props":381,"children":382},{},[383,394,413,418,423],{"type":81,"tag":94,"props":384,"children":385},{},[386,388,392],{"type":87,"value":387},"Remove the trusted publisher on ",{"type":81,"tag":294,"props":389,"children":390},{"href":296},[391],{"type":87,"value":299},{"type":87,"value":393}," to stop further publishes.",{"type":81,"tag":94,"props":395,"children":396},{},[397,399,404,406,411],{"type":87,"value":398},"Deprecate the version with a clear message and point ",{"type":81,"tag":100,"props":400,"children":401},{},[402],{"type":87,"value":403},"latest",{"type":87,"value":405}," at the last\ngood version (",{"type":81,"tag":100,"props":407,"children":408},{},[409],{"type":87,"value":410},"npm dist-tag add",{"type":87,"value":412},"). Contact npm support to remove it; npm\nremoves malware quickly.",{"type":81,"tag":94,"props":414,"children":415},{},[416],{"type":87,"value":417},"Find how it happened (approved run, compromised account, changed workflow)\nand close that path.",{"type":81,"tag":94,"props":419,"children":420},{},[421],{"type":87,"value":422},"Publish a GitHub advisory so users get alerts.",{"type":81,"tag":94,"props":424,"children":425},{},[426],{"type":87,"value":427},"Restore the trusted publisher and release a clean version through the\nnormal flow.",{"type":81,"tag":82,"props":429,"children":431},{"id":430},"a-dependency-is-compromised",[432],{"type":87,"value":433},"A dependency is compromised",{"type":81,"tag":174,"props":435,"children":436},{},[437,439,444,446,450],{"type":87,"value":438},"Check whether the bad version is in any lockfile (",{"type":81,"tag":100,"props":440,"children":441},{},[442],{"type":87,"value":443},"pnpm why \u003Cpackage>",{"type":87,"value":445},"). If\nthe quarantine held it back, there is nothing to do. If it was installed,\ntreat any machine or CI run that installed it as exposed: rotate the\ncredentials that were reachable there. In CI, jobs that install dependencies\nnever hold publish rights, and none of them can push. ",{"type":81,"tag":100,"props":447,"children":448},{},[449],{"type":87,"value":116},{"type":87,"value":451}," splits the\nVersion packages pull request into three jobs:",{"type":81,"tag":90,"props":453,"children":454},{},[455,465,496],{"type":81,"tag":94,"props":456,"children":457},{},[458,463],{"type":81,"tag":191,"props":459,"children":460},{},[461],{"type":87,"value":462},"Prepare version",{"type":87,"value":464}," runs dependency code (the Changesets CLI and its\nchangelog plugin) with a read-only token. A compromised dependency there can\nonly change the patch it uploads.",{"type":81,"tag":94,"props":466,"children":467},{},[468,473,475,480,482,487,489,494],{"type":81,"tag":191,"props":469,"children":470},{},[471],{"type":87,"value":472},"Version packages",{"type":87,"value":474}," holds the token that pushes ",{"type":81,"tag":100,"props":476,"children":477},{},[478],{"type":87,"value":479},"changeset-release\u002Fmain",{"type":87,"value":481},"\nand opens the pull request, but installs and runs nothing from the\nrepository. It fails if the patch changes anything in a ",{"type":81,"tag":100,"props":483,"children":484},{},[485],{"type":87,"value":486},"package.json",{"type":87,"value":488},"\nbesides its version and the ranges of the repository's own packages, or\ntouches anything besides ",{"type":81,"tag":100,"props":490,"children":491},{},[492],{"type":87,"value":493},"CHANGELOG.md",{"type":87,"value":495}," files and changeset files.",{"type":81,"tag":94,"props":497,"children":498},{},[499,501,505],{"type":87,"value":500},"A third job, which checks out and installs nothing, starts ",{"type":81,"tag":100,"props":502,"children":503},{},[504],{"type":87,"value":149},{"type":87,"value":506}," on that\nbranch.",{"type":81,"tag":174,"props":508,"children":509},{},[510,512,517,519,523],{"type":87,"value":511},"A tampered patch can therefore change only versions, changelogs and changeset\nfiles; a changed script, dependency or ",{"type":81,"tag":100,"props":513,"children":514},{},[515],{"type":87,"value":516},"files",{"type":87,"value":518}," entry fails the job. A tampered\nchangelog still reaches the pull request, and code reaches ",{"type":81,"tag":100,"props":520,"children":521},{},[522],{"type":87,"value":142},{"type":87,"value":524}," only when a\nperson merges it, so look at the Version packages diff before you merge. Check recent branches and the\nVersion packages pull request for unexpected changes.",{"title":526,"searchDepth":527,"depth":527,"links":528},"",4,[529,531,532,533,534],{"id":84,"depth":530,"text":88},2,{"id":181,"depth":530,"text":184},{"id":243,"depth":530,"text":246},{"id":376,"depth":530,"text":379},{"id":430,"depth":530,"text":433},"markdown","docs",{"source":538,"path":539,"stem":540,"dir":536,"extension":541},"content","docs\u002F8.security.md","docs\u002F8.security","md",{"requestedPath":53,"resolvedPath":53,"alternates":543},[],{"requested":545,"resolved":546,"usedFallback":9},{},{"locale":8},{"previous":548,"next":549},{"title":43,"id":44,"canonicalKey":45,"locale":8,"draft":9,"path":47},{"title":55,"id":56,"canonicalKey":57,"locale":8,"draft":9,"path":59},1791448771350]