Endurecimento de Lançamento de Pacotes npm
Objetivo
Levar um pacote npm de "funciona localmente" para "honesto, inspecionável e seguro para publicar". Priorizar verificação real sobre impressões: conteúdo do tarball do pacote, contrato de runtime, CI, configurações do GitHub e higiene de atualização de dependências.
Comece Aqui
- Confirme o repositório exato:
pwd
git status --short --branch
git remote -v
- Inspecione a superfície do pacote:
cat package.json
ls -la
find .github -maxdepth 3 -type f -print 2>/dev/null
- Detecte o gerenciador de pacotes a partir dos lockfiles:
package-lock.json-> npmpnpm-lock.yaml-> pnpmyarn.lock-> yarn
Use os comandos npm abaixo como padrão, mas traduza para o gerenciador de pacotes detectado quando necessário.
Contrato do Pacote
Faça o package.json dizer a verdade:
name: o alvo de publicação é intencional e disponívelversion: versão de publicação pretendida atualdescription,keywords: úteis o suficiente para pesquisa no registrolicense: presente e corresponde aoLICENSErepository,homepage,bugs: apontam para o repositório público. Se a URL do repositório público ainda não for final, sinalize-as como um TODO explícito de pré-publicação, em vez de omiti-las silenciosamentetype,exports,main,bin: correspondem ao que os usuários importarão ou executarãofiles: limita o tarball a artefatos publicáveisengines.node: runtime mínimo que o pacote realmente suportaprepublishOnlyou equivalente: impede a publicação de saída de build desatualizada
Para pacotes CLI, confirme que o alvo de bin está construído, executável onde relevante e testado superficialmente através do comando que os usuários executarão.
Regra do Piso de Runtime
O engine do Node é uma promessa, não uma decoração.
- Se
engines.nodediz>=20, o CI deve testar o Node 20. - Mantenha
@types/nodena mesma linha principal da versão de runtime mínima suportada. - Prefira fixar exatamente
@types/nodeno patch mais recente dessa linha de runtime. - Não mescle bumps principais de
@types/nodeacima do piso de runtime, a menos que você também eleveengines.node. engines.nodeé consultivo: o npm apenas avisa e não bloqueia a instalação a menos que o usuário definaengine-strict, então o CI na versão do piso é o que realmente impõe a promessa.
Por quê: os tipos do Node são apenas para desenvolvimento, mas eles mudam o que o TypeScript permite. @types/node@26 pode permitir que o código compile enquanto usa APIs que falham para usuários no Node 20.
Comandos de Verificação
Para projetos npm:
npm ci
npm run build --if-present
npm test --if-present
npm audit --omit=dev
npm pack --dry-run
Para pnpm/yarn, use os comandos equivalentes de install/build/test/audit/pack. Se o suporte a audit diferir, diga isso em vez de fingir.
Para CLIs, execute também pelo menos um teste de fumaça de fora para dentro:
node dist/path/to/cli.js --help
npm pack --dry-run
Se prático, instale o .tgz empacotado em um diretório temporário e execute o binário exatamente como um usuário faria.
Revisão do Tarball
npm pack --dry-run é a superfície de verdade do registro. Verifique se:
- inclui saída construída
- inclui
README.mdeLICENSE - exclui apenas lixo de código-fonte, arquivos temporários, testes, documentos privados, fixtures, bancos de dados locais, segredos
- o tamanho do pacote é razoável
- o caminho
binda CLI aparece no tarball
Se files estiver faltando, adicione-o. Não confie em .gitignore como política de publicação.
Linha de Base do GitHub
Adicione ou verifique:
.github/workflows/ci.yml.github/dependabot.yml.github/CODEOWNERS.github/pull_request_template.mdSECURITY.md
Use versões principais atuais suportadas para ações de propriedade do GitHub. Se o Actions avisar que um runtime de ação está obsoleto, atualize a ação principal após o CI passar.
O CI geralmente deve incluir:
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
test:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@CURRENT_MAJOR
- uses: actions/setup-node@CURRENT_MAJOR
with:
node-version: 20
cache: npm
- run: npm ci
- run: npm run build --if-present
- run: npm test --if-present
Ajuste node-version para o runtime mínimo suportado do pacote e substitua cada @CURRENT_MAJOR pela tag principal atual da ação (por exemplo actions/checkout@v4) — fixe em uma versão real, nunca confirme o placeholder.
Linha de Base do Dependabot
Configure ambas as atualizações de pacote e GitHub Actions:
version: 2
updates:
- package-ecosystem: npm
directory: /
schedule:
interval: weekly
open-pull-requests-limit: 5
commit-message:
prefix: deps
include: scope
- package-ecosystem: github-actions
directory: /
schedule:
interval: weekly
open-pull-requests-limit: 5
commit-message:
prefix: deps
include: scope
Quando o Dependabot abrir PRs imediatamente após a configuração, trate isso como prova de que a configuração está ativa. Trie os PRs; não mescle cegamente todas as verificações verdes.
Triagem do Dependabot
Use esta regra prática:
- Patch/menor de dependência de runtime: mesclar se as verificações passarem e as notas de versão forem irrelevantes.
- Maior de dependência de runtime: inspecionar notas de versão e testar superficialmente o comportamento real do pacote.
- Maior de ferramenta de desenvolvimento: mesclar se build/test/pack passarem e não alterar o contrato de runtime.
@types/nodeacima do piso de runtime: fechar ou substituir por uma fixação no piso de runtime.- Maior de GitHub Actions: mesclar se o CI passar e remover avisos de depreciação.
Se dois PRs entrarem em conflito em um arquivo de configuração pequeno, mescle um e atualize o outro, ou aplique o segundo diretamente com um comentário claro explicando que foi substituído.
Endurecimento Remoto do GitHub
Aplique o que o repositório/conta suportar e, em seguida, verifique as configurações reais com gh ou leituras da API.
Configurações úteis:
gh repo edit OWNER/REPO \
--delete-branch-on-merge \
--enable-squash-merge \
--enable-merge-commit=false \
--enable-rebase-merge=false \
--allow-update-branch
gh api --method PUT repos/OWNER/REPO/vulnerability-alerts --silent
gh api --method PUT repos/OWNER/REPO/automated-security-fixes --silent
gh api --method PUT repos/OWNER/REPO/actions/permissions/workflow \
-f default_workflow_permissions=read \
-F can_approve_pull_request_reviews=false --silent
Para listas de permissões de Actions, prefira ações de propriedade do GitHub apenas quando o workflow usar apenas ações de propriedade do GitHub. Se ações de terceiros forem necessárias, permita explicitamente o menor conjunto.
Repositórios públicos geralmente desbloqueiam mais controles de segurança. Repositórios privados em planos gratuitos podem bloquear:
- proteção de branch ou conjuntos de regras
- varredura de código
- varredura de segredos
- proteção de push
Diga claramente os bloqueadores. Não relate controles indisponíveis como habilitados.
Proteção de Branch
Quando disponível, proteja o branch padrão com:
- exigir pull request antes de mesclar
- exigir verificações de status de CI
- exigir que branches estejam atualizados se isso se adequar ao projeto
- exigir resolução de conversa
- bloquear pushes forçados
- bloquear deleções
- exigir histórico linear se estiver usando apenas squash merges
Após ativar a proteção, pare de fazer push direto para main a menos que o usuário peça explicitamente e entenda o desvio.
Ferramentas de PR Bloqueado
Se estiver usando uma ferramenta automatizada de revisão/bloqueio, certifique-se de que o remoto tenha um branch padrão/base antes de pedir ao bloqueio para criar um PR. Novos remotos vazios geralmente precisam de um push inicial para main primeiro; caso contrário, o bloqueio pode validar e enviar um branch de funcionalidade, mas falhar na criação do PR porque não há uma ref base.
Lista de Verificação para Publicação
Antes de npm publish:
npm whoamimostra a conta pretendidanpm view NOME_DO_PACOTE name versionconfirma o estado do nome- CI verde no commit que está sendo publicado
npm pack --dry-runfoi revisado- Exemplos de instalação/uso do README correspondem ao comportamento real
repository,homepageebugsresolvem para o repositório público real (ou são explicitamente sinalizados como TODO se a URL não for final)- Nenhum alerta de segurança/Dependabot que bloqueie o lançamento permanece
- O aumento de versão é intencional, marcado no git (
git tag vX.Y.Z && git push --tags) e refletido em um CHANGELOG ou lançamento do GitHub - Pacotes com escopo (
@scope/nome) publicam com--access public(pacotes com escopo padrão são restritos/privados)
Para contas que usam autenticação web/passkey/2FA:
npm publish --auth-type=web
Prefira publicar do CI com proveniência para um link de cadeia de suprimentos verificável. A proveniência requer um pacote público, um CI suportado como GitHub Actions e permissão id-token: write no job:
npm publish --provenance --access public
Após a publicação:
npm view NOME_DO_PACOTE version
npm view NOME_DO_PACOTE bin files engines repository --json
npx -y NOME_DO_PACOTE --help
Se publicado com proveniência, confirme que o registro mostra a atestação de proveniência / "Built and signed" na página do pacote.
Resposta Final
Relate apenas o que foi realmente verificado:
- verificações de pacote executadas
- resultado do tarball
- decisões de PR de dependência
- commits/PRs criados ou mesclados
- configurações do GitHub confirmadas
- controles bloqueados pelo plano/visibilidade
- tag de lançamento e status de proveniência, se publicado
- comando de publicação ou verificação pós-publicação se ainda de propriedade do usuário