[{"data":1,"prerenderedAt":4253},["ShallowReactive",2],{"blog":3},[4,2432,2700],{"id":5,"title":6,"body":7,"date":2419,"description":2420,"extension":2421,"meta":2422,"navigation":813,"path":2423,"seo":2424,"stem":2425,"tags":2426,"__hash__":2431},"blog/blog/claude-code-seguranca-configuracoes-que-protegem-seu-codigo.md","Claude Code: as configurações de segurança que protegem seu código de si mesmo",{"type":8,"value":9,"toc":2401},"minimark",[10,22,34,37,42,45,56,66,76,82,86,101,248,268,272,281,287,344,365,369,372,455,469,473,482,545,564,570,604,607,611,622,637,739,750,755,979,982,992,996,1107,1111,1128,1241,1245,1252,1341,1345,1633,1649,1653,2201,2216,2225,2234,2238,2246,2287,2294,2299,2303,2306,2337,2340,2343,2347,2350,2388,2394,2397],[11,12,13,14,21],"p",{},"A maior parte do que vou explicar aqui veio do ",[15,16,20],"a",{"href":17,"rel":18},"https://anthropic.skilljar.com/claude-code-in-action",[19],"nofollow","curso oficial da Anthropic sobre Claude Code",". Recomendo pra quem quer entender o ferramental completo. O que fiz foi aplicar no meu workflow e adicionar camadas extras de proteção que fazem sentido pro meu contexto.",[11,23,24,25,29,30,33],{},"No artigo anterior, falei sobre como usar skills pra transformar o Claude Code num auditor de segurança. Mas tem um problema anterior a isso: o próprio Claude Code tem acesso ao seu sistema de arquivos. Ele lê arquivos, executa comandos, escreve código. Se não estiver configurado direito, ele pode ler seu ",[26,27,28],"code",{},".env",", exibir suas chaves de API no output, ou rodar um ",[26,31,32],{},"rm -rf"," sem pensar duas vezes.",[11,35,36],{},"A segurança do código que o Claude Code audita não adianta nada se o ambiente onde ele roda não for seguro.",[38,39,41],"h2",{"id":40},"as-três-raízes-de-configuração","As três raízes de configuração",[11,43,44],{},"O Claude Code lê configurações de três lugares, em ordem de prioridade:",[11,46,47,55],{},[48,49,50,51,54],"strong",{},"1. ",[26,52,53],{},"~/.claude/settings.json"," (global)"," — Vale pra todos os projetos. É onde ficam regras pessoais: permissões padrão, hooks globais, preferências de workflow. Fica na home e não é versionado.",[11,57,58,65],{},[48,59,60,61,64],{},"2. ",[26,62,63],{},".claude/settings.json"," (projeto)"," — Fica na raiz do repositório. Sobrescreve o global. É onde ficam regras específicas do projeto: deny rules pra arquivos sensíveis daquele repo, hooks de validação, restrições de time. Versione no git. Assim todos que trabalham no repositório herdam as mesmas restrições.",[11,67,68,75],{},[48,69,70,71,74],{},"3. ",[26,72,73],{},"CLAUDE.md"," (instruções em linguagem natural)"," — Também na raiz do repositório. Não é um JSON de permissões. São instruções em markdown que o Claude Code absorve no início de cada sessão. Regras de estilo, convenções de código, e sim, regras de segurança.",[11,77,78,79,81],{},"A hierarquia funciona assim: o global define o baseline. O projeto restringe ou ajusta. O ",[26,80,73],{}," complementa com contexto semântico que o JSON não consegue expressar.",[38,83,85],{"id":84},"permissions-allow-e-deny","Permissions: allow e deny",[11,87,88,89,92,93,96,97,100],{},"A estrutura principal é o campo ",[26,90,91],{},"permissions",", com duas listas: ",[26,94,95],{},"allow"," e ",[26,98,99],{},"deny",".",[102,103,108],"pre",{"className":104,"code":105,"language":106,"meta":107,"style":107},"language-json shiki shiki-themes github-light github-dark","{\n  \"permissions\": {\n    \"allow\": [\n      \"Bash(*)\",\n      \"Read(*)\",\n      \"Edit(*)\",\n      \"Write(*)\",\n      \"Glob(*)\",\n      \"Grep(*)\"\n    ],\n    \"deny\": [\n      \"Read(./.env)\",\n      \"Read(./.env.*)\",\n      \"Read(./secrets/**)\",\n      \"Read(./config/credentials.json)\"\n    ]\n  }\n}\n","json","",[26,109,110,119,129,138,148,156,164,172,180,186,192,200,208,216,224,230,236,242],{"__ignoreMap":107},[111,112,115],"span",{"class":113,"line":114},"line",1,[111,116,118],{"class":117},"sVt8B","{\n",[111,120,122,126],{"class":113,"line":121},2,[111,123,125],{"class":124},"sj4cs","  \"permissions\"",[111,127,128],{"class":117},": {\n",[111,130,132,135],{"class":113,"line":131},3,[111,133,134],{"class":124},"    \"allow\"",[111,136,137],{"class":117},": [\n",[111,139,141,145],{"class":113,"line":140},4,[111,142,144],{"class":143},"sZZnC","      \"Bash(*)\"",[111,146,147],{"class":117},",\n",[111,149,151,154],{"class":113,"line":150},5,[111,152,153],{"class":143},"      \"Read(*)\"",[111,155,147],{"class":117},[111,157,159,162],{"class":113,"line":158},6,[111,160,161],{"class":143},"      \"Edit(*)\"",[111,163,147],{"class":117},[111,165,167,170],{"class":113,"line":166},7,[111,168,169],{"class":143},"      \"Write(*)\"",[111,171,147],{"class":117},[111,173,175,178],{"class":113,"line":174},8,[111,176,177],{"class":143},"      \"Glob(*)\"",[111,179,147],{"class":117},[111,181,183],{"class":113,"line":182},9,[111,184,185],{"class":143},"      \"Grep(*)\"\n",[111,187,189],{"class":113,"line":188},10,[111,190,191],{"class":117},"    ],\n",[111,193,195,198],{"class":113,"line":194},11,[111,196,197],{"class":124},"    \"deny\"",[111,199,137],{"class":117},[111,201,203,206],{"class":113,"line":202},12,[111,204,205],{"class":143},"      \"Read(./.env)\"",[111,207,147],{"class":117},[111,209,211,214],{"class":113,"line":210},13,[111,212,213],{"class":143},"      \"Read(./.env.*)\"",[111,215,147],{"class":117},[111,217,219,222],{"class":113,"line":218},14,[111,220,221],{"class":143},"      \"Read(./secrets/**)\"",[111,223,147],{"class":117},[111,225,227],{"class":113,"line":226},15,[111,228,229],{"class":143},"      \"Read(./config/credentials.json)\"\n",[111,231,233],{"class":113,"line":232},16,[111,234,235],{"class":117},"    ]\n",[111,237,239],{"class":113,"line":238},17,[111,240,241],{"class":117},"  }\n",[111,243,245],{"class":113,"line":244},18,[111,246,247],{"class":117},"}\n",[11,249,250,251,253,254,256,257,260,261,264,265,267],{},"O ",[26,252,95],{}," define o que o Claude Code pode fazer sem pedir confirmação. O ",[26,255,99],{}," bloqueia absolutamente. Deny sempre ganha de allow. Não importa se o allow tem ",[26,258,259],{},"Read(*)",". Se o deny tem ",[26,262,263],{},"Read(./.env)",", o ",[26,266,28],{}," não é lido.",[38,269,271],{"id":270},"protegendo-variáveis-de-ambiente","Protegendo variáveis de ambiente",[11,273,274,275,277,278,280],{},"O cenário mais comum: o Claude Code precisa ler seu código pra entender o projeto, mas o ",[26,276,28],{}," tem chaves de banco de dados, tokens de API, secrets de autenticação. Se ele ler o ",[26,279,28],{}," e depois for escrever um teste ou documentar algo, aquelas chaves podem aparecer no output, no código gerado, ou pior, num commit.",[11,282,283,284,286],{},"Mas bloquear só a leitura não basta. O Claude Code também pode sobrescrever um ",[26,285,28],{}," existente ou criar um novo com valores errados. As deny rules precisam cobrir as três operações: Read, Write e Edit.",[102,288,290],{"className":104,"code":289,"language":106,"meta":107,"style":107},"\"deny\": [\n  \"Read(./.env)\",\n  \"Read(./.env.*)\",\n  \"Write(./.env)\",\n  \"Write(./.env.*)\",\n  \"Edit(./.env)\",\n  \"Edit(./.env.*)\"\n]\n",[26,291,292,299,306,313,320,327,334,339],{"__ignoreMap":107},[111,293,294,297],{"class":113,"line":114},[111,295,296],{"class":143},"\"deny\"",[111,298,137],{"class":117},[111,300,301,304],{"class":113,"line":121},[111,302,303],{"class":143},"  \"Read(./.env)\"",[111,305,147],{"class":117},[111,307,308,311],{"class":113,"line":131},[111,309,310],{"class":143},"  \"Read(./.env.*)\"",[111,312,147],{"class":117},[111,314,315,318],{"class":113,"line":140},[111,316,317],{"class":143},"  \"Write(./.env)\"",[111,319,147],{"class":117},[111,321,322,325],{"class":113,"line":150},[111,323,324],{"class":143},"  \"Write(./.env.*)\"",[111,326,147],{"class":117},[111,328,329,332],{"class":113,"line":158},[111,330,331],{"class":143},"  \"Edit(./.env)\"",[111,333,147],{"class":117},[111,335,336],{"class":113,"line":166},[111,337,338],{"class":143},"  \"Edit(./.env.*)\"\n",[111,340,341],{"class":113,"line":174},[111,342,343],{"class":117},"]\n",[11,345,346,347,350,351,354,355,354,358,354,361,364],{},"O padrão ",[26,348,349],{},".env.*"," cobre ",[26,352,353],{},".env.local",", ",[26,356,357],{},".env.production",[26,359,360],{},".env.staging",[26,362,363],{},".env.development",". Mas se preferir ser explícito, liste cada um. Explícito é mais seguro que glob quando se trata de secrets.",[38,366,368],{"id":367},"protegendo-diretórios-de-secrets","Protegendo diretórios de secrets",[11,370,371],{},"Muitos projetos mantêm certificados, chaves privadas ou arquivos de credenciais em diretórios dedicados. A mesma lógica: bloquear Read, Write e Edit.",[102,373,375],{"className":104,"code":374,"language":106,"meta":107,"style":107},"\"deny\": [\n  \"Read(./secrets/**)\",\n  \"Write(./secrets/**)\",\n  \"Edit(./secrets/**)\",\n  \"Read(./config/credentials.json)\",\n  \"Write(./config/credentials.json)\",\n  \"Edit(./config/credentials.json)\",\n  \"Read(./**/*.pem)\",\n  \"Read(./**/*.key)\",\n  \"Read(./**/*serviceAccountKey*)\",\n  \"Read(./**/credentials*.json)\"\n]\n",[26,376,377,383,390,397,404,411,418,425,432,439,446,451],{"__ignoreMap":107},[111,378,379,381],{"class":113,"line":114},[111,380,296],{"class":143},[111,382,137],{"class":117},[111,384,385,388],{"class":113,"line":121},[111,386,387],{"class":143},"  \"Read(./secrets/**)\"",[111,389,147],{"class":117},[111,391,392,395],{"class":113,"line":131},[111,393,394],{"class":143},"  \"Write(./secrets/**)\"",[111,396,147],{"class":117},[111,398,399,402],{"class":113,"line":140},[111,400,401],{"class":143},"  \"Edit(./secrets/**)\"",[111,403,147],{"class":117},[111,405,406,409],{"class":113,"line":150},[111,407,408],{"class":143},"  \"Read(./config/credentials.json)\"",[111,410,147],{"class":117},[111,412,413,416],{"class":113,"line":158},[111,414,415],{"class":143},"  \"Write(./config/credentials.json)\"",[111,417,147],{"class":117},[111,419,420,423],{"class":113,"line":166},[111,421,422],{"class":143},"  \"Edit(./config/credentials.json)\"",[111,424,147],{"class":117},[111,426,427,430],{"class":113,"line":174},[111,428,429],{"class":143},"  \"Read(./**/*.pem)\"",[111,431,147],{"class":117},[111,433,434,437],{"class":113,"line":182},[111,435,436],{"class":143},"  \"Read(./**/*.key)\"",[111,438,147],{"class":117},[111,440,441,444],{"class":113,"line":188},[111,442,443],{"class":143},"  \"Read(./**/*serviceAccountKey*)\"",[111,445,147],{"class":117},[111,447,448],{"class":113,"line":194},[111,449,450],{"class":143},"  \"Read(./**/credentials*.json)\"\n",[111,452,453],{"class":113,"line":202},[111,454,343],{"class":117},[11,456,250,457,460,461,464,465,468],{},[26,458,459],{},"**"," faz match recursivo. ",[26,462,463],{},"Read(./**/*.pem)"," bloqueia qualquer arquivo ",[26,466,467],{},".pem"," em qualquer subdiretório. Assim, mesmo que alguém adicione um certificado numa pasta inesperada, o Claude Code não lê.",[38,470,472],{"id":471},"bloqueando-comandos-destrutivos","Bloqueando comandos destrutivos",[11,474,250,475,477,478,481],{},[26,476,95],{}," para Bash aceita glob patterns no argumento. Em vez de ",[26,479,480],{},"Bash(*)",", que permite qualquer comando, é possível ser mais restritivo:",[102,483,485],{"className":104,"code":484,"language":106,"meta":107,"style":107},"\"allow\": [\n  \"Bash(npm test*)\",\n  \"Bash(npm run*)\",\n  \"Bash(git status*)\",\n  \"Bash(git diff*)\",\n  \"Bash(git log*)\",\n  \"Bash(ls*)\",\n  \"Bash(cat package.json)\"\n]\n",[26,486,487,494,501,508,515,522,529,536,541],{"__ignoreMap":107},[111,488,489,492],{"class":113,"line":114},[111,490,491],{"class":143},"\"allow\"",[111,493,137],{"class":117},[111,495,496,499],{"class":113,"line":121},[111,497,498],{"class":143},"  \"Bash(npm test*)\"",[111,500,147],{"class":117},[111,502,503,506],{"class":113,"line":131},[111,504,505],{"class":143},"  \"Bash(npm run*)\"",[111,507,147],{"class":117},[111,509,510,513],{"class":113,"line":140},[111,511,512],{"class":143},"  \"Bash(git status*)\"",[111,514,147],{"class":117},[111,516,517,520],{"class":113,"line":150},[111,518,519],{"class":143},"  \"Bash(git diff*)\"",[111,521,147],{"class":117},[111,523,524,527],{"class":113,"line":158},[111,525,526],{"class":143},"  \"Bash(git log*)\"",[111,528,147],{"class":117},[111,530,531,534],{"class":113,"line":166},[111,532,533],{"class":143},"  \"Bash(ls*)\"",[111,535,147],{"class":117},[111,537,538],{"class":113,"line":174},[111,539,540],{"class":143},"  \"Bash(cat package.json)\"\n",[111,542,543],{"class":113,"line":182},[111,544,343],{"class":117},[11,546,547,548,550,551,354,554,354,557,354,560,563],{},"Sem o ",[26,549,480],{},", qualquer comando que não esteja na lista de allow vai pedir confirmação antes de executar. Isso significa que ",[26,552,553],{},"rm",[26,555,556],{},"curl",[26,558,559],{},"docker",[26,561,562],{},"kubectl"," e qualquer outro comando potencialmente destrutivo precisa de aprovação explícita.",[11,565,566,567,569],{},"Mesmo com ",[26,568,480],{}," liberado, o deny pode bloquear comandos específicos:",[102,571,573],{"className":104,"code":572,"language":106,"meta":107,"style":107},"\"deny\": [\n  \"Bash(rm -rf *)\",\n  \"Bash(git push --force*)\",\n  \"Bash(git reset --hard*)\"\n]\n",[26,574,575,581,588,595,600],{"__ignoreMap":107},[111,576,577,579],{"class":113,"line":114},[111,578,296],{"class":143},[111,580,137],{"class":117},[111,582,583,586],{"class":113,"line":121},[111,584,585],{"class":143},"  \"Bash(rm -rf *)\"",[111,587,147],{"class":117},[111,589,590,593],{"class":113,"line":131},[111,591,592],{"class":143},"  \"Bash(git push --force*)\"",[111,594,147],{"class":117},[111,596,597],{"class":113,"line":140},[111,598,599],{"class":143},"  \"Bash(git reset --hard*)\"\n",[111,601,602],{"class":113,"line":150},[111,603,343],{"class":117},[11,605,606],{},"Essa é a abordagem que faz mais sentido pra quem já tem experiência. Liberar tudo e bloquear o que é perigoso.",[38,608,610],{"id":609},"hooks-a-segunda-camada-de-proteção","Hooks: a segunda camada de proteção",[11,612,613,614,617,618,621],{},"Deny rules protegem por ",[48,615,616],{},"path"," e por ",[48,619,620],{},"comando",". Mas e se o Claude Code ler um secret de outra fonte (um log, um output de comando, uma resposta de API) e tentar escrevê-lo num arquivo de código? O deny por path não pega isso. O hook pega.",[11,623,624,625,628,629,632,633,636],{},"Hooks são scripts que rodam automaticamente antes ou depois de ações do Claude Code. O formato no ",[26,626,627],{},"settings.json"," usa ",[26,630,631],{},"matcher"," pra filtrar a ferramenta e ",[26,634,635],{},"hooks"," como array de handlers:",[102,638,640],{"className":104,"code":639,"language":106,"meta":107,"style":107},"{\n  \"hooks\": {\n    \"PreToolUse\": [\n      {\n        \"matcher\": \"Bash\",\n        \"hooks\": [\n          {\n            \"type\": \"command\",\n            \"command\": \"~/.claude/hooks/meu-script.sh\"\n          }\n        ]\n      }\n    ]\n  }\n}\n",[26,641,642,646,653,660,665,678,685,690,702,712,717,722,727,731,735],{"__ignoreMap":107},[111,643,644],{"class":113,"line":114},[111,645,118],{"class":117},[111,647,648,651],{"class":113,"line":121},[111,649,650],{"class":124},"  \"hooks\"",[111,652,128],{"class":117},[111,654,655,658],{"class":113,"line":131},[111,656,657],{"class":124},"    \"PreToolUse\"",[111,659,137],{"class":117},[111,661,662],{"class":113,"line":140},[111,663,664],{"class":117},"      {\n",[111,666,667,670,673,676],{"class":113,"line":150},[111,668,669],{"class":124},"        \"matcher\"",[111,671,672],{"class":117},": ",[111,674,675],{"class":143},"\"Bash\"",[111,677,147],{"class":117},[111,679,680,683],{"class":113,"line":158},[111,681,682],{"class":124},"        \"hooks\"",[111,684,137],{"class":117},[111,686,687],{"class":113,"line":166},[111,688,689],{"class":117},"          {\n",[111,691,692,695,697,700],{"class":113,"line":174},[111,693,694],{"class":124},"            \"type\"",[111,696,672],{"class":117},[111,698,699],{"class":143},"\"command\"",[111,701,147],{"class":117},[111,703,704,707,709],{"class":113,"line":182},[111,705,706],{"class":124},"            \"command\"",[111,708,672],{"class":117},[111,710,711],{"class":143},"\"~/.claude/hooks/meu-script.sh\"\n",[111,713,714],{"class":113,"line":188},[111,715,716],{"class":117},"          }\n",[111,718,719],{"class":113,"line":194},[111,720,721],{"class":117},"        ]\n",[111,723,724],{"class":113,"line":202},[111,725,726],{"class":117},"      }\n",[111,728,729],{"class":113,"line":210},[111,730,235],{"class":117},[111,732,733],{"class":113,"line":218},[111,734,241],{"class":117},[111,736,737],{"class":113,"line":226},[111,738,247],{"class":117},[11,740,741,742,745,746,749],{},"O script recebe o input da ferramenta via ",[48,743,744],{},"stdin como JSON",". Pra bloquear uma ação, o script retorna um JSON com ",[26,747,748],{},"permissionDecision: \"deny\"",". Pra permitir, basta retornar exit code 0.",[751,752,754],"h3",{"id":753},"script-detectando-secrets-no-conteúdo","Script: detectando secrets no conteúdo",[102,756,760],{"className":757,"code":758,"language":759,"meta":107,"style":107},"language-bash shiki shiki-themes github-light github-dark","#!/bin/bash\n# ~/.claude/hooks/block-secrets.sh\n# Ignora arquivos markdown (podem conter exemplos de patterns)\nFILE_PATH=$(jq -r '.tool_input.file_path // .tool_input.filePath // \"\"' 2>/dev/null)\n\nif echo \"$FILE_PATH\" | grep -qiE '\\.(md|mdx|txt|rst)$'; then\n  exit 0\nfi\n\nCONTENT=$(jq -r '.tool_input | to_entries | map(.value) | join(\" \")' 2>/dev/null)\n\nif echo \"$CONTENT\" | grep -qiE 'AKIA[0-9A-Z]{16}|sk-[a-zA-Z0-9]{20,}|ghp_[a-zA-Z0-9]{36}|mongodb\\+srv://|BEGIN.*PRIVATE KEY|xox[bpas]-'; then\n  jq -n '{\n    hookSpecificOutput: {\n      hookEventName: \"PreToolUse\",\n      permissionDecision: \"deny\",\n      permissionDecisionReason: \"Secret detectado no conteudo\"\n    }\n  }'\nelse\n  exit 0\nfi\n","bash",[26,761,762,768,773,778,809,815,850,858,863,867,889,893,919,930,935,940,945,950,955,961,967,974],{"__ignoreMap":107},[111,763,764],{"class":113,"line":114},[111,765,767],{"class":766},"sJ8bj","#!/bin/bash\n",[111,769,770],{"class":113,"line":121},[111,771,772],{"class":766},"# ~/.claude/hooks/block-secrets.sh\n",[111,774,775],{"class":113,"line":131},[111,776,777],{"class":766},"# Ignora arquivos markdown (podem conter exemplos de patterns)\n",[111,779,780,783,787,790,794,797,800,803,806],{"class":113,"line":140},[111,781,782],{"class":117},"FILE_PATH",[111,784,786],{"class":785},"szBVR","=",[111,788,789],{"class":117},"$(",[111,791,793],{"class":792},"sScJk","jq",[111,795,796],{"class":124}," -r",[111,798,799],{"class":143}," '.tool_input.file_path // .tool_input.filePath // \"\"'",[111,801,802],{"class":785}," 2>",[111,804,805],{"class":143},"/dev/null",[111,807,808],{"class":117},")\n",[111,810,811],{"class":113,"line":150},[111,812,814],{"emptyLinePlaceholder":813},true,"\n",[111,816,817,820,823,826,829,832,835,838,841,844,847],{"class":113,"line":158},[111,818,819],{"class":785},"if",[111,821,822],{"class":124}," echo",[111,824,825],{"class":143}," \"",[111,827,828],{"class":117},"$FILE_PATH",[111,830,831],{"class":143},"\"",[111,833,834],{"class":785}," |",[111,836,837],{"class":792}," grep",[111,839,840],{"class":124}," -qiE",[111,842,843],{"class":143}," '\\.(md|mdx|txt|rst)$'",[111,845,846],{"class":117},"; ",[111,848,849],{"class":785},"then\n",[111,851,852,855],{"class":113,"line":166},[111,853,854],{"class":124},"  exit",[111,856,857],{"class":124}," 0\n",[111,859,860],{"class":113,"line":174},[111,861,862],{"class":785},"fi\n",[111,864,865],{"class":113,"line":182},[111,866,814],{"emptyLinePlaceholder":813},[111,868,869,872,874,876,878,880,883,885,887],{"class":113,"line":188},[111,870,871],{"class":117},"CONTENT",[111,873,786],{"class":785},[111,875,789],{"class":117},[111,877,793],{"class":792},[111,879,796],{"class":124},[111,881,882],{"class":143}," '.tool_input | to_entries | map(.value) | join(\" \")'",[111,884,802],{"class":785},[111,886,805],{"class":143},[111,888,808],{"class":117},[111,890,891],{"class":113,"line":194},[111,892,814],{"emptyLinePlaceholder":813},[111,894,895,897,899,901,904,906,908,910,912,915,917],{"class":113,"line":202},[111,896,819],{"class":785},[111,898,822],{"class":124},[111,900,825],{"class":143},[111,902,903],{"class":117},"$CONTENT",[111,905,831],{"class":143},[111,907,834],{"class":785},[111,909,837],{"class":792},[111,911,840],{"class":124},[111,913,914],{"class":143}," 'AKIA[0-9A-Z]{16}|sk-[a-zA-Z0-9]{20,}|ghp_[a-zA-Z0-9]{36}|mongodb\\+srv://|BEGIN.*PRIVATE KEY|xox[bpas]-'",[111,916,846],{"class":117},[111,918,849],{"class":785},[111,920,921,924,927],{"class":113,"line":210},[111,922,923],{"class":792},"  jq",[111,925,926],{"class":124}," -n",[111,928,929],{"class":143}," '{\n",[111,931,932],{"class":113,"line":218},[111,933,934],{"class":143},"    hookSpecificOutput: {\n",[111,936,937],{"class":113,"line":226},[111,938,939],{"class":143},"      hookEventName: \"PreToolUse\",\n",[111,941,942],{"class":113,"line":232},[111,943,944],{"class":143},"      permissionDecision: \"deny\",\n",[111,946,947],{"class":113,"line":238},[111,948,949],{"class":143},"      permissionDecisionReason: \"Secret detectado no conteudo\"\n",[111,951,952],{"class":113,"line":244},[111,953,954],{"class":143},"    }\n",[111,956,958],{"class":113,"line":957},19,[111,959,960],{"class":143},"  }'\n",[111,962,964],{"class":113,"line":963},20,[111,965,966],{"class":785},"else\n",[111,968,970,972],{"class":113,"line":969},21,[111,971,854],{"class":124},[111,973,857],{"class":124},[111,975,977],{"class":113,"line":976},22,[111,978,862],{"class":785},[11,980,981],{},"Esse hook roda antes de qualquer Write, Edit ou MultiEdit. Verifica se o conteúdo contém patterns de AWS Access Keys, OpenAI/Stripe keys, GitHub tokens, connection strings MongoDB, chaves privadas e Slack tokens. Se detectar, bloqueia a escrita.",[11,983,984,985,96,988,991],{},"Detalhe importante: o script ignora arquivos ",[26,986,987],{},".md",[26,989,990],{},".mdx",". Sem isso, um blog post que mencione esses patterns como exemplo (como este que estou escrevendo agora) seria bloqueado. Na prática, isso aconteceu comigo ao escrever este artigo. O hook bloqueou a escrita do próprio post porque o conteúdo continha os patterns de exemplo. A solução foi adicionar o filtro por extensão no topo do script.",[751,993,995],{"id":994},"script-protegendo-env-via-writeedit","Script: protegendo .env via Write/Edit",[102,997,999],{"className":757,"code":998,"language":759,"meta":107,"style":107},"#!/bin/bash\n# ~/.claude/hooks/block-env-write.sh\nFILE_PATH=$(jq -r '.tool_input.file_path // .tool_input.filePath // \"\"' 2>/dev/null)\n\nif echo \"$FILE_PATH\" | grep -qE '\\.env($|\\.)'; then\n  jq -n '{\n    hookSpecificOutput: {\n      hookEventName: \"PreToolUse\",\n      permissionDecision: \"deny\",\n      permissionDecisionReason: \"Escrita em arquivo .env bloqueada por hook\"\n    }\n  }'\nelse\n  exit 0\nfi\n",[26,1000,1001,1005,1010,1030,1034,1060,1068,1072,1076,1080,1085,1089,1093,1097,1103],{"__ignoreMap":107},[111,1002,1003],{"class":113,"line":114},[111,1004,767],{"class":766},[111,1006,1007],{"class":113,"line":121},[111,1008,1009],{"class":766},"# ~/.claude/hooks/block-env-write.sh\n",[111,1011,1012,1014,1016,1018,1020,1022,1024,1026,1028],{"class":113,"line":131},[111,1013,782],{"class":117},[111,1015,786],{"class":785},[111,1017,789],{"class":117},[111,1019,793],{"class":792},[111,1021,796],{"class":124},[111,1023,799],{"class":143},[111,1025,802],{"class":785},[111,1027,805],{"class":143},[111,1029,808],{"class":117},[111,1031,1032],{"class":113,"line":140},[111,1033,814],{"emptyLinePlaceholder":813},[111,1035,1036,1038,1040,1042,1044,1046,1048,1050,1053,1056,1058],{"class":113,"line":150},[111,1037,819],{"class":785},[111,1039,822],{"class":124},[111,1041,825],{"class":143},[111,1043,828],{"class":117},[111,1045,831],{"class":143},[111,1047,834],{"class":785},[111,1049,837],{"class":792},[111,1051,1052],{"class":124}," -qE",[111,1054,1055],{"class":143}," '\\.env($|\\.)'",[111,1057,846],{"class":117},[111,1059,849],{"class":785},[111,1061,1062,1064,1066],{"class":113,"line":158},[111,1063,923],{"class":792},[111,1065,926],{"class":124},[111,1067,929],{"class":143},[111,1069,1070],{"class":113,"line":166},[111,1071,934],{"class":143},[111,1073,1074],{"class":113,"line":174},[111,1075,939],{"class":143},[111,1077,1078],{"class":113,"line":182},[111,1079,944],{"class":143},[111,1081,1082],{"class":113,"line":188},[111,1083,1084],{"class":143},"      permissionDecisionReason: \"Escrita em arquivo .env bloqueada por hook\"\n",[111,1086,1087],{"class":113,"line":194},[111,1088,954],{"class":143},[111,1090,1091],{"class":113,"line":202},[111,1092,960],{"class":143},[111,1094,1095],{"class":113,"line":210},[111,1096,966],{"class":785},[111,1098,1099,1101],{"class":113,"line":218},[111,1100,854],{"class":124},[111,1102,857],{"class":124},[111,1104,1105],{"class":113,"line":226},[111,1106,862],{"class":785},[751,1108,1110],{"id":1109},"script-protegendo-env-via-bash","Script: protegendo .env via Bash",[11,1112,1113,1114,96,1116,1119,1120,1123,1124,1127],{},"O deny bloqueia ",[26,1115,263],{},[26,1117,1118],{},"Write(./.env)",", mas o Claude Code pode tentar ",[26,1121,1122],{},"cat .env"," ou ",[26,1125,1126],{},"echo \"KEY=value\" >> .env"," pelo Bash. O hook pega esses caminhos indiretos:",[102,1129,1131],{"className":757,"code":1130,"language":759,"meta":107,"style":107},"#!/bin/bash\n# ~/.claude/hooks/block-env-bash.sh\nCOMMAND=$(jq -r '.tool_input.command' 2>/dev/null)\n\nif echo \"$COMMAND\" | grep -qiE '>\\s*\\.env|>>\\s*\\.env|cat\\s+\\.env|head\\s+\\.env|tail\\s+\\.env|source\\s+\\.env|cp\\s.*\\.env|mv\\s.*\\.env|printenv'; then\n  jq -n '{\n    hookSpecificOutput: {\n      hookEventName: \"PreToolUse\",\n      permissionDecision: \"deny\",\n      permissionDecisionReason: \"Comando manipula ou expoe arquivo .env\"\n    }\n  }'\nelse\n  exit 0\nfi\n",[26,1132,1133,1137,1142,1164,1168,1194,1202,1206,1210,1214,1219,1223,1227,1231,1237],{"__ignoreMap":107},[111,1134,1135],{"class":113,"line":114},[111,1136,767],{"class":766},[111,1138,1139],{"class":113,"line":121},[111,1140,1141],{"class":766},"# ~/.claude/hooks/block-env-bash.sh\n",[111,1143,1144,1147,1149,1151,1153,1155,1158,1160,1162],{"class":113,"line":131},[111,1145,1146],{"class":117},"COMMAND",[111,1148,786],{"class":785},[111,1150,789],{"class":117},[111,1152,793],{"class":792},[111,1154,796],{"class":124},[111,1156,1157],{"class":143}," '.tool_input.command'",[111,1159,802],{"class":785},[111,1161,805],{"class":143},[111,1163,808],{"class":117},[111,1165,1166],{"class":113,"line":140},[111,1167,814],{"emptyLinePlaceholder":813},[111,1169,1170,1172,1174,1176,1179,1181,1183,1185,1187,1190,1192],{"class":113,"line":150},[111,1171,819],{"class":785},[111,1173,822],{"class":124},[111,1175,825],{"class":143},[111,1177,1178],{"class":117},"$COMMAND",[111,1180,831],{"class":143},[111,1182,834],{"class":785},[111,1184,837],{"class":792},[111,1186,840],{"class":124},[111,1188,1189],{"class":143}," '>\\s*\\.env|>>\\s*\\.env|cat\\s+\\.env|head\\s+\\.env|tail\\s+\\.env|source\\s+\\.env|cp\\s.*\\.env|mv\\s.*\\.env|printenv'",[111,1191,846],{"class":117},[111,1193,849],{"class":785},[111,1195,1196,1198,1200],{"class":113,"line":158},[111,1197,923],{"class":792},[111,1199,926],{"class":124},[111,1201,929],{"class":143},[111,1203,1204],{"class":113,"line":166},[111,1205,934],{"class":143},[111,1207,1208],{"class":113,"line":174},[111,1209,939],{"class":143},[111,1211,1212],{"class":113,"line":182},[111,1213,944],{"class":143},[111,1215,1216],{"class":113,"line":188},[111,1217,1218],{"class":143},"      permissionDecisionReason: \"Comando manipula ou expoe arquivo .env\"\n",[111,1220,1221],{"class":113,"line":194},[111,1222,954],{"class":143},[111,1224,1225],{"class":113,"line":202},[111,1226,960],{"class":143},[111,1228,1229],{"class":113,"line":210},[111,1230,966],{"class":785},[111,1232,1233,1235],{"class":113,"line":218},[111,1234,854],{"class":124},[111,1236,857],{"class":124},[111,1238,1239],{"class":113,"line":226},[111,1240,862],{"class":785},[751,1242,1244],{"id":1243},"script-audit-log","Script: audit log",[11,1246,1247,1248,1251],{},"Um hook ",[26,1249,1250],{},"PostToolUse"," que loga todos os comandos Bash executados. Não bloqueia nada, só registra:",[102,1253,1255],{"className":757,"code":1254,"language":759,"meta":107,"style":107},"#!/bin/bash\n# ~/.claude/hooks/audit-log.sh\nCOMMAND=$(jq -r '.tool_input.command // \"unknown\"' 2>/dev/null)\necho \"$(date -u +%Y-%m-%dT%H:%M:%SZ) | Bash | $(echo \"$COMMAND\" | head -c 200)\" >> ~/.claude/audit.log\nexit 0\n",[26,1256,1257,1261,1266,1287,1334],{"__ignoreMap":107},[111,1258,1259],{"class":113,"line":114},[111,1260,767],{"class":766},[111,1262,1263],{"class":113,"line":121},[111,1264,1265],{"class":766},"# ~/.claude/hooks/audit-log.sh\n",[111,1267,1268,1270,1272,1274,1276,1278,1281,1283,1285],{"class":113,"line":131},[111,1269,1146],{"class":117},[111,1271,786],{"class":785},[111,1273,789],{"class":117},[111,1275,793],{"class":792},[111,1277,796],{"class":124},[111,1279,1280],{"class":143}," '.tool_input.command // \"unknown\"'",[111,1282,802],{"class":785},[111,1284,805],{"class":143},[111,1286,808],{"class":117},[111,1288,1289,1292,1295,1298,1301,1304,1306,1308,1310,1313,1316,1319,1322,1325,1328,1331],{"class":113,"line":140},[111,1290,1291],{"class":124},"echo",[111,1293,1294],{"class":143}," \"$(",[111,1296,1297],{"class":792},"date",[111,1299,1300],{"class":124}," -u",[111,1302,1303],{"class":143}," +%Y-%m-%dT%H:%M:%SZ) | Bash | $(",[111,1305,1291],{"class":124},[111,1307,825],{"class":143},[111,1309,1178],{"class":117},[111,1311,1312],{"class":143},"\" ",[111,1314,1315],{"class":785},"|",[111,1317,1318],{"class":792}," head",[111,1320,1321],{"class":124}," -c",[111,1323,1324],{"class":124}," 200",[111,1326,1327],{"class":143},")\"",[111,1329,1330],{"class":785}," >>",[111,1332,1333],{"class":143}," ~/.claude/audit.log\n",[111,1335,1336,1339],{"class":113,"line":150},[111,1337,1338],{"class":124},"exit",[111,1340,857],{"class":124},[751,1342,1344],{"id":1343},"a-configuração-dos-hooks-no-settingsjson","A configuração dos hooks no settings.json",[102,1346,1348],{"className":104,"code":1347,"language":106,"meta":107,"style":107},"{\n  \"hooks\": {\n    \"PreToolUse\": [\n      {\n        \"matcher\": \"Write|Edit|MultiEdit\",\n        \"hooks\": [\n          {\n            \"type\": \"command\",\n            \"command\": \"~/.claude/hooks/block-secrets.sh\"\n          }\n        ]\n      },\n      {\n        \"matcher\": \"Write|Edit|MultiEdit\",\n        \"hooks\": [\n          {\n            \"type\": \"command\",\n            \"command\": \"~/.claude/hooks/block-env-write.sh\"\n          }\n        ]\n      },\n      {\n        \"matcher\": \"Bash\",\n        \"hooks\": [\n          {\n            \"type\": \"command\",\n            \"command\": \"~/.claude/hooks/block-env-bash.sh\"\n          }\n        ]\n      }\n    ],\n    \"PostToolUse\": [\n      {\n        \"matcher\": \"Bash\",\n        \"hooks\": [\n          {\n            \"type\": \"command\",\n            \"command\": \"~/.claude/hooks/audit-log.sh\"\n          }\n        ]\n      }\n    ]\n  }\n}\n",[26,1349,1350,1354,1360,1366,1370,1381,1387,1391,1401,1410,1414,1418,1423,1427,1437,1443,1447,1457,1466,1470,1474,1478,1482,1493,1500,1505,1516,1526,1531,1536,1541,1546,1554,1559,1570,1577,1582,1593,1603,1608,1613,1618,1623,1628],{"__ignoreMap":107},[111,1351,1352],{"class":113,"line":114},[111,1353,118],{"class":117},[111,1355,1356,1358],{"class":113,"line":121},[111,1357,650],{"class":124},[111,1359,128],{"class":117},[111,1361,1362,1364],{"class":113,"line":131},[111,1363,657],{"class":124},[111,1365,137],{"class":117},[111,1367,1368],{"class":113,"line":140},[111,1369,664],{"class":117},[111,1371,1372,1374,1376,1379],{"class":113,"line":150},[111,1373,669],{"class":124},[111,1375,672],{"class":117},[111,1377,1378],{"class":143},"\"Write|Edit|MultiEdit\"",[111,1380,147],{"class":117},[111,1382,1383,1385],{"class":113,"line":158},[111,1384,682],{"class":124},[111,1386,137],{"class":117},[111,1388,1389],{"class":113,"line":166},[111,1390,689],{"class":117},[111,1392,1393,1395,1397,1399],{"class":113,"line":174},[111,1394,694],{"class":124},[111,1396,672],{"class":117},[111,1398,699],{"class":143},[111,1400,147],{"class":117},[111,1402,1403,1405,1407],{"class":113,"line":182},[111,1404,706],{"class":124},[111,1406,672],{"class":117},[111,1408,1409],{"class":143},"\"~/.claude/hooks/block-secrets.sh\"\n",[111,1411,1412],{"class":113,"line":188},[111,1413,716],{"class":117},[111,1415,1416],{"class":113,"line":194},[111,1417,721],{"class":117},[111,1419,1420],{"class":113,"line":202},[111,1421,1422],{"class":117},"      },\n",[111,1424,1425],{"class":113,"line":210},[111,1426,664],{"class":117},[111,1428,1429,1431,1433,1435],{"class":113,"line":218},[111,1430,669],{"class":124},[111,1432,672],{"class":117},[111,1434,1378],{"class":143},[111,1436,147],{"class":117},[111,1438,1439,1441],{"class":113,"line":226},[111,1440,682],{"class":124},[111,1442,137],{"class":117},[111,1444,1445],{"class":113,"line":232},[111,1446,689],{"class":117},[111,1448,1449,1451,1453,1455],{"class":113,"line":238},[111,1450,694],{"class":124},[111,1452,672],{"class":117},[111,1454,699],{"class":143},[111,1456,147],{"class":117},[111,1458,1459,1461,1463],{"class":113,"line":244},[111,1460,706],{"class":124},[111,1462,672],{"class":117},[111,1464,1465],{"class":143},"\"~/.claude/hooks/block-env-write.sh\"\n",[111,1467,1468],{"class":113,"line":957},[111,1469,716],{"class":117},[111,1471,1472],{"class":113,"line":963},[111,1473,721],{"class":117},[111,1475,1476],{"class":113,"line":969},[111,1477,1422],{"class":117},[111,1479,1480],{"class":113,"line":976},[111,1481,664],{"class":117},[111,1483,1485,1487,1489,1491],{"class":113,"line":1484},23,[111,1486,669],{"class":124},[111,1488,672],{"class":117},[111,1490,675],{"class":143},[111,1492,147],{"class":117},[111,1494,1496,1498],{"class":113,"line":1495},24,[111,1497,682],{"class":124},[111,1499,137],{"class":117},[111,1501,1503],{"class":113,"line":1502},25,[111,1504,689],{"class":117},[111,1506,1508,1510,1512,1514],{"class":113,"line":1507},26,[111,1509,694],{"class":124},[111,1511,672],{"class":117},[111,1513,699],{"class":143},[111,1515,147],{"class":117},[111,1517,1519,1521,1523],{"class":113,"line":1518},27,[111,1520,706],{"class":124},[111,1522,672],{"class":117},[111,1524,1525],{"class":143},"\"~/.claude/hooks/block-env-bash.sh\"\n",[111,1527,1529],{"class":113,"line":1528},28,[111,1530,716],{"class":117},[111,1532,1534],{"class":113,"line":1533},29,[111,1535,721],{"class":117},[111,1537,1539],{"class":113,"line":1538},30,[111,1540,726],{"class":117},[111,1542,1544],{"class":113,"line":1543},31,[111,1545,191],{"class":117},[111,1547,1549,1552],{"class":113,"line":1548},32,[111,1550,1551],{"class":124},"    \"PostToolUse\"",[111,1553,137],{"class":117},[111,1555,1557],{"class":113,"line":1556},33,[111,1558,664],{"class":117},[111,1560,1562,1564,1566,1568],{"class":113,"line":1561},34,[111,1563,669],{"class":124},[111,1565,672],{"class":117},[111,1567,675],{"class":143},[111,1569,147],{"class":117},[111,1571,1573,1575],{"class":113,"line":1572},35,[111,1574,682],{"class":124},[111,1576,137],{"class":117},[111,1578,1580],{"class":113,"line":1579},36,[111,1581,689],{"class":117},[111,1583,1585,1587,1589,1591],{"class":113,"line":1584},37,[111,1586,694],{"class":124},[111,1588,672],{"class":117},[111,1590,699],{"class":143},[111,1592,147],{"class":117},[111,1594,1596,1598,1600],{"class":113,"line":1595},38,[111,1597,706],{"class":124},[111,1599,672],{"class":117},[111,1601,1602],{"class":143},"\"~/.claude/hooks/audit-log.sh\"\n",[111,1604,1606],{"class":113,"line":1605},39,[111,1607,716],{"class":117},[111,1609,1611],{"class":113,"line":1610},40,[111,1612,721],{"class":117},[111,1614,1616],{"class":113,"line":1615},41,[111,1617,726],{"class":117},[111,1619,1621],{"class":113,"line":1620},42,[111,1622,235],{"class":117},[111,1624,1626],{"class":113,"line":1625},43,[111,1627,241],{"class":117},[111,1629,1631],{"class":113,"line":1630},44,[111,1632,247],{"class":117},[11,1634,1635,1636,1638,1639,1642,1643,1645,1646,1648],{},"Cada matcher filtra a ferramenta. O array ",[26,1637,635],{}," contém os handlers com ",[26,1640,1641],{},"type: \"command\""," e o path do script. Os scripts leem JSON do stdin com ",[26,1644,793],{}," e retornam JSON com ",[26,1647,748],{}," pra bloquear ou exit code 0 pra permitir.",[38,1650,1652],{"id":1651},"a-configuração-completa-que-uso","A configuração completa que uso",[102,1654,1656],{"className":104,"code":1655,"language":106,"meta":107,"style":107},"{\n  \"permissions\": {\n    \"allow\": [\n      \"Bash(*)\",\n      \"Read(*)\",\n      \"Edit(*)\",\n      \"Write(*)\",\n      \"MultiEdit(*)\",\n      \"LS(**)\",\n      \"Glob(*)\",\n      \"Grep(*)\",\n      \"NotebookRead(*)\",\n      \"NotebookEdit(*)\",\n      \"TodoRead(*)\",\n      \"TodoWrite(*)\",\n      \"WebSearch(*)\",\n      \"WebFetch(*)\"\n    ],\n    \"deny\": [\n      \"Read(./.env)\",\n      \"Read(./.env.*)\",\n      \"Write(./.env)\",\n      \"Write(./.env.*)\",\n      \"Edit(./.env)\",\n      \"Edit(./.env.*)\",\n      \"Read(./secrets/**)\",\n      \"Write(./secrets/**)\",\n      \"Edit(./secrets/**)\",\n      \"Read(./config/credentials.json)\",\n      \"Write(./config/credentials.json)\",\n      \"Edit(./config/credentials.json)\",\n      \"Read(./**/*.pem)\",\n      \"Read(./**/*.key)\",\n      \"Read(./**/*serviceAccountKey*)\",\n      \"Read(./**/credentials*.json)\",\n      \"Bash(rm -rf *)\",\n      \"Bash(git push --force*)\",\n      \"Bash(git reset --hard*)\"\n    ]\n  },\n  \"hooks\": {\n    \"PreToolUse\": [\n      {\n        \"matcher\": \"Write|Edit|MultiEdit\",\n        \"hooks\": [\n          {\n            \"type\": \"command\",\n            \"command\": \"~/.claude/hooks/block-secrets.sh\"\n          }\n        ]\n      },\n      {\n        \"matcher\": \"Write|Edit|MultiEdit\",\n        \"hooks\": [\n          {\n            \"type\": \"command\",\n            \"command\": \"~/.claude/hooks/block-env-write.sh\"\n          }\n        ]\n      },\n      {\n        \"matcher\": \"Bash\",\n        \"hooks\": [\n          {\n            \"type\": \"command\",\n            \"command\": \"~/.claude/hooks/block-env-bash.sh\"\n          }\n        ]\n      }\n    ],\n    \"PostToolUse\": [\n      {\n        \"matcher\": \"Bash\",\n        \"hooks\": [\n          {\n            \"type\": \"command\",\n            \"command\": \"~/.claude/hooks/audit-log.sh\"\n          }\n        ]\n      }\n    ]\n  }\n}\n",[26,1657,1658,1662,1668,1674,1680,1686,1692,1698,1705,1712,1718,1725,1732,1739,1746,1753,1760,1765,1769,1775,1781,1787,1794,1801,1808,1815,1821,1828,1835,1842,1849,1856,1863,1870,1877,1884,1891,1898,1903,1907,1912,1918,1924,1928,1938,1945,1950,1961,1970,1975,1980,1985,1990,2001,2008,2013,2024,2033,2038,2043,2048,2053,2064,2071,2076,2087,2096,2101,2106,2111,2116,2123,2128,2139,2146,2151,2162,2171,2176,2181,2186,2191,2196],{"__ignoreMap":107},[111,1659,1660],{"class":113,"line":114},[111,1661,118],{"class":117},[111,1663,1664,1666],{"class":113,"line":121},[111,1665,125],{"class":124},[111,1667,128],{"class":117},[111,1669,1670,1672],{"class":113,"line":131},[111,1671,134],{"class":124},[111,1673,137],{"class":117},[111,1675,1676,1678],{"class":113,"line":140},[111,1677,144],{"class":143},[111,1679,147],{"class":117},[111,1681,1682,1684],{"class":113,"line":150},[111,1683,153],{"class":143},[111,1685,147],{"class":117},[111,1687,1688,1690],{"class":113,"line":158},[111,1689,161],{"class":143},[111,1691,147],{"class":117},[111,1693,1694,1696],{"class":113,"line":166},[111,1695,169],{"class":143},[111,1697,147],{"class":117},[111,1699,1700,1703],{"class":113,"line":174},[111,1701,1702],{"class":143},"      \"MultiEdit(*)\"",[111,1704,147],{"class":117},[111,1706,1707,1710],{"class":113,"line":182},[111,1708,1709],{"class":143},"      \"LS(**)\"",[111,1711,147],{"class":117},[111,1713,1714,1716],{"class":113,"line":188},[111,1715,177],{"class":143},[111,1717,147],{"class":117},[111,1719,1720,1723],{"class":113,"line":194},[111,1721,1722],{"class":143},"      \"Grep(*)\"",[111,1724,147],{"class":117},[111,1726,1727,1730],{"class":113,"line":202},[111,1728,1729],{"class":143},"      \"NotebookRead(*)\"",[111,1731,147],{"class":117},[111,1733,1734,1737],{"class":113,"line":210},[111,1735,1736],{"class":143},"      \"NotebookEdit(*)\"",[111,1738,147],{"class":117},[111,1740,1741,1744],{"class":113,"line":218},[111,1742,1743],{"class":143},"      \"TodoRead(*)\"",[111,1745,147],{"class":117},[111,1747,1748,1751],{"class":113,"line":226},[111,1749,1750],{"class":143},"      \"TodoWrite(*)\"",[111,1752,147],{"class":117},[111,1754,1755,1758],{"class":113,"line":232},[111,1756,1757],{"class":143},"      \"WebSearch(*)\"",[111,1759,147],{"class":117},[111,1761,1762],{"class":113,"line":238},[111,1763,1764],{"class":143},"      \"WebFetch(*)\"\n",[111,1766,1767],{"class":113,"line":244},[111,1768,191],{"class":117},[111,1770,1771,1773],{"class":113,"line":957},[111,1772,197],{"class":124},[111,1774,137],{"class":117},[111,1776,1777,1779],{"class":113,"line":963},[111,1778,205],{"class":143},[111,1780,147],{"class":117},[111,1782,1783,1785],{"class":113,"line":969},[111,1784,213],{"class":143},[111,1786,147],{"class":117},[111,1788,1789,1792],{"class":113,"line":976},[111,1790,1791],{"class":143},"      \"Write(./.env)\"",[111,1793,147],{"class":117},[111,1795,1796,1799],{"class":113,"line":1484},[111,1797,1798],{"class":143},"      \"Write(./.env.*)\"",[111,1800,147],{"class":117},[111,1802,1803,1806],{"class":113,"line":1495},[111,1804,1805],{"class":143},"      \"Edit(./.env)\"",[111,1807,147],{"class":117},[111,1809,1810,1813],{"class":113,"line":1502},[111,1811,1812],{"class":143},"      \"Edit(./.env.*)\"",[111,1814,147],{"class":117},[111,1816,1817,1819],{"class":113,"line":1507},[111,1818,221],{"class":143},[111,1820,147],{"class":117},[111,1822,1823,1826],{"class":113,"line":1518},[111,1824,1825],{"class":143},"      \"Write(./secrets/**)\"",[111,1827,147],{"class":117},[111,1829,1830,1833],{"class":113,"line":1528},[111,1831,1832],{"class":143},"      \"Edit(./secrets/**)\"",[111,1834,147],{"class":117},[111,1836,1837,1840],{"class":113,"line":1533},[111,1838,1839],{"class":143},"      \"Read(./config/credentials.json)\"",[111,1841,147],{"class":117},[111,1843,1844,1847],{"class":113,"line":1538},[111,1845,1846],{"class":143},"      \"Write(./config/credentials.json)\"",[111,1848,147],{"class":117},[111,1850,1851,1854],{"class":113,"line":1543},[111,1852,1853],{"class":143},"      \"Edit(./config/credentials.json)\"",[111,1855,147],{"class":117},[111,1857,1858,1861],{"class":113,"line":1548},[111,1859,1860],{"class":143},"      \"Read(./**/*.pem)\"",[111,1862,147],{"class":117},[111,1864,1865,1868],{"class":113,"line":1556},[111,1866,1867],{"class":143},"      \"Read(./**/*.key)\"",[111,1869,147],{"class":117},[111,1871,1872,1875],{"class":113,"line":1561},[111,1873,1874],{"class":143},"      \"Read(./**/*serviceAccountKey*)\"",[111,1876,147],{"class":117},[111,1878,1879,1882],{"class":113,"line":1572},[111,1880,1881],{"class":143},"      \"Read(./**/credentials*.json)\"",[111,1883,147],{"class":117},[111,1885,1886,1889],{"class":113,"line":1579},[111,1887,1888],{"class":143},"      \"Bash(rm -rf *)\"",[111,1890,147],{"class":117},[111,1892,1893,1896],{"class":113,"line":1584},[111,1894,1895],{"class":143},"      \"Bash(git push --force*)\"",[111,1897,147],{"class":117},[111,1899,1900],{"class":113,"line":1595},[111,1901,1902],{"class":143},"      \"Bash(git reset --hard*)\"\n",[111,1904,1905],{"class":113,"line":1605},[111,1906,235],{"class":117},[111,1908,1909],{"class":113,"line":1610},[111,1910,1911],{"class":117},"  },\n",[111,1913,1914,1916],{"class":113,"line":1615},[111,1915,650],{"class":124},[111,1917,128],{"class":117},[111,1919,1920,1922],{"class":113,"line":1620},[111,1921,657],{"class":124},[111,1923,137],{"class":117},[111,1925,1926],{"class":113,"line":1625},[111,1927,664],{"class":117},[111,1929,1930,1932,1934,1936],{"class":113,"line":1630},[111,1931,669],{"class":124},[111,1933,672],{"class":117},[111,1935,1378],{"class":143},[111,1937,147],{"class":117},[111,1939,1941,1943],{"class":113,"line":1940},45,[111,1942,682],{"class":124},[111,1944,137],{"class":117},[111,1946,1948],{"class":113,"line":1947},46,[111,1949,689],{"class":117},[111,1951,1953,1955,1957,1959],{"class":113,"line":1952},47,[111,1954,694],{"class":124},[111,1956,672],{"class":117},[111,1958,699],{"class":143},[111,1960,147],{"class":117},[111,1962,1964,1966,1968],{"class":113,"line":1963},48,[111,1965,706],{"class":124},[111,1967,672],{"class":117},[111,1969,1409],{"class":143},[111,1971,1973],{"class":113,"line":1972},49,[111,1974,716],{"class":117},[111,1976,1978],{"class":113,"line":1977},50,[111,1979,721],{"class":117},[111,1981,1983],{"class":113,"line":1982},51,[111,1984,1422],{"class":117},[111,1986,1988],{"class":113,"line":1987},52,[111,1989,664],{"class":117},[111,1991,1993,1995,1997,1999],{"class":113,"line":1992},53,[111,1994,669],{"class":124},[111,1996,672],{"class":117},[111,1998,1378],{"class":143},[111,2000,147],{"class":117},[111,2002,2004,2006],{"class":113,"line":2003},54,[111,2005,682],{"class":124},[111,2007,137],{"class":117},[111,2009,2011],{"class":113,"line":2010},55,[111,2012,689],{"class":117},[111,2014,2016,2018,2020,2022],{"class":113,"line":2015},56,[111,2017,694],{"class":124},[111,2019,672],{"class":117},[111,2021,699],{"class":143},[111,2023,147],{"class":117},[111,2025,2027,2029,2031],{"class":113,"line":2026},57,[111,2028,706],{"class":124},[111,2030,672],{"class":117},[111,2032,1465],{"class":143},[111,2034,2036],{"class":113,"line":2035},58,[111,2037,716],{"class":117},[111,2039,2041],{"class":113,"line":2040},59,[111,2042,721],{"class":117},[111,2044,2046],{"class":113,"line":2045},60,[111,2047,1422],{"class":117},[111,2049,2051],{"class":113,"line":2050},61,[111,2052,664],{"class":117},[111,2054,2056,2058,2060,2062],{"class":113,"line":2055},62,[111,2057,669],{"class":124},[111,2059,672],{"class":117},[111,2061,675],{"class":143},[111,2063,147],{"class":117},[111,2065,2067,2069],{"class":113,"line":2066},63,[111,2068,682],{"class":124},[111,2070,137],{"class":117},[111,2072,2074],{"class":113,"line":2073},64,[111,2075,689],{"class":117},[111,2077,2079,2081,2083,2085],{"class":113,"line":2078},65,[111,2080,694],{"class":124},[111,2082,672],{"class":117},[111,2084,699],{"class":143},[111,2086,147],{"class":117},[111,2088,2090,2092,2094],{"class":113,"line":2089},66,[111,2091,706],{"class":124},[111,2093,672],{"class":117},[111,2095,1525],{"class":143},[111,2097,2099],{"class":113,"line":2098},67,[111,2100,716],{"class":117},[111,2102,2104],{"class":113,"line":2103},68,[111,2105,721],{"class":117},[111,2107,2109],{"class":113,"line":2108},69,[111,2110,726],{"class":117},[111,2112,2114],{"class":113,"line":2113},70,[111,2115,191],{"class":117},[111,2117,2119,2121],{"class":113,"line":2118},71,[111,2120,1551],{"class":124},[111,2122,137],{"class":117},[111,2124,2126],{"class":113,"line":2125},72,[111,2127,664],{"class":117},[111,2129,2131,2133,2135,2137],{"class":113,"line":2130},73,[111,2132,669],{"class":124},[111,2134,672],{"class":117},[111,2136,675],{"class":143},[111,2138,147],{"class":117},[111,2140,2142,2144],{"class":113,"line":2141},74,[111,2143,682],{"class":124},[111,2145,137],{"class":117},[111,2147,2149],{"class":113,"line":2148},75,[111,2150,689],{"class":117},[111,2152,2154,2156,2158,2160],{"class":113,"line":2153},76,[111,2155,694],{"class":124},[111,2157,672],{"class":117},[111,2159,699],{"class":143},[111,2161,147],{"class":117},[111,2163,2165,2167,2169],{"class":113,"line":2164},77,[111,2166,706],{"class":124},[111,2168,672],{"class":117},[111,2170,1602],{"class":143},[111,2172,2174],{"class":113,"line":2173},78,[111,2175,716],{"class":117},[111,2177,2179],{"class":113,"line":2178},79,[111,2180,721],{"class":117},[111,2182,2184],{"class":113,"line":2183},80,[111,2185,726],{"class":117},[111,2187,2189],{"class":113,"line":2188},81,[111,2190,235],{"class":117},[111,2192,2194],{"class":113,"line":2193},82,[111,2195,241],{"class":117},[111,2197,2199],{"class":113,"line":2198},83,[111,2200,247],{"class":117},[11,2202,2203,2204,354,2206,354,2208,2211,2212,2215],{},"Sim, o allow é amplo. ",[26,2205,480],{},[26,2207,259],{},[26,2209,2210],{},"Write(*)",". Tudo liberado. Trabalho sozinho nos meus projetos, conheço o que cada comando faz, e travar o Claude Code pra pedir confirmação a cada ",[26,2213,2214],{},"ls"," destrói a produtividade.",[11,2217,2218,2219,2221,2222,2224],{},"Mas permissão ampla não significa sem proteção. O deny list bloqueia os arquivos sensíveis (",[26,2220,28],{},", secrets, credenciais, chaves privadas). Os hooks validam o conteúdo antes de qualquer escrita e bloqueiam comandos que manipulam ",[26,2223,28],{}," pelo shell. O audit log registra tudo que foi executado.",[11,2226,2227,2228,2230,2231,2233],{},"A diferença entre ser permissivo por descuido e ser permissivo de forma controlada é ter as camadas de proteção configuradas. O ",[26,2229,480],{}," sem deny rules e sem hooks é um risco. O ",[26,2232,480],{}," com deny list, hooks de validação e audit log é uma decisão consciente de quem entende os vetores de ataque e escolheu onde colocar as barreiras.",[38,2235,2237],{"id":2236},"claudemd-a-terceira-camada","CLAUDE.md: a terceira camada",[11,2239,250,2240,2242,2243,2245],{},[26,2241,73],{}," na raiz do projeto complementa o ",[26,2244,627],{}," com regras em linguagem natural. O JSON define permissões binárias (pode/não pode). O markdown define comportamento (como agir).",[102,2247,2251],{"className":2248,"code":2249,"language":2250,"meta":107,"style":107},"language-markdown shiki shiki-themes github-light github-dark","## Segurança\n\n- Nunca incluir secrets, tokens ou chaves em código ou documentação\n- Nunca fazer commit de arquivos .env\n- Usar variáveis de ambiente (process.env) para toda configuração sensível\n- Nunca logar dados sensíveis (senhas, tokens, PII)\n- Sanitizar todo input antes de usar em queries\n","markdown",[26,2252,2253,2258,2262,2267,2272,2277,2282],{"__ignoreMap":107},[111,2254,2255],{"class":113,"line":114},[111,2256,2257],{},"## Segurança\n",[111,2259,2260],{"class":113,"line":121},[111,2261,814],{"emptyLinePlaceholder":813},[111,2263,2264],{"class":113,"line":131},[111,2265,2266],{},"- Nunca incluir secrets, tokens ou chaves em código ou documentação\n",[111,2268,2269],{"class":113,"line":140},[111,2270,2271],{},"- Nunca fazer commit de arquivos .env\n",[111,2273,2274],{"class":113,"line":150},[111,2275,2276],{},"- Usar variáveis de ambiente (process.env) para toda configuração sensível\n",[111,2278,2279],{"class":113,"line":158},[111,2280,2281],{},"- Nunca logar dados sensíveis (senhas, tokens, PII)\n",[111,2283,2284],{"class":113,"line":166},[111,2285,2286],{},"- Sanitizar todo input antes de usar em queries\n",[11,2288,2289,2290,2293],{},"O Claude Code lê essas regras no início de cada sessão e as aplica em tudo que faz. Se for criar um teste que precisa de uma API key, vai usar ",[26,2291,2292],{},"process.env.API_KEY"," ou um mock em vez de um valor hardcoded.",[11,2295,250,2296,2298],{},[26,2297,73],{}," pega o que o JSON não consegue expressar. \"Nunca logar dados sensíveis\" não é uma permissão. É um comportamento. O Claude Code entende isso e aplica em todo código que escreve.",[38,2300,2302],{"id":2301},"o-que-acontece-se-não-configurar-nada","O que acontece se não configurar nada",[11,2304,2305],{},"Sem deny rules e sem hooks, o Claude Code pode:",[2307,2308,2309,2316,2319,2322,2325,2331],"ol",{},[2310,2311,2312,2313,2315],"li",{},"Ler o ",[26,2314,28],{}," quando tentar entender a configuração do projeto",[2310,2317,2318],{},"Incluir valores reais de variáveis de ambiente em exemplos de código",[2310,2320,2321],{},"Exibir connection strings no output ao explicar como o banco funciona",[2310,2323,2324],{},"Copiar secrets pra arquivos de teste ou documentação",[2310,2326,2327,2328,2330],{},"Sobrescrever o ",[26,2329,28],{}," com valores diferentes ao tentar \"ajudar\"",[2310,2332,2333,2334,2336],{},"Rodar ",[26,2335,1122],{}," pelo Bash, contornando deny rules de Read",[11,2338,2339],{},"Nenhum desses cenários é malicioso. O Claude Code não está tentando roubar secrets. Ele simplesmente não sabe que aquele arquivo não deveria ser lido a menos que exista uma regra dizendo isso.",[11,2341,2342],{},"A configuração padrão do Claude Code é conservadora. Ele pede confirmação pra quase tudo. Mas quando as permissões são liberadas pra aumentar a produtividade (o que é normal e recomendado), o deny list e os hooks se tornam a última linha de defesa.",[38,2344,2346],{"id":2345},"as-quatro-camadas","As quatro camadas",[11,2348,2349],{},"Resumindo, a proteção funciona em camadas:",[2307,2351,2352,2361,2375,2383],{},[2310,2353,2354,2357,2358,2360],{},[48,2355,2356],{},"Deny rules por path"," — bloqueia Read/Write/Edit em ",[26,2359,28],{},", secrets, credenciais, chaves privadas",[2310,2362,2363,2366,2367,354,2369,354,2372],{},[48,2364,2365],{},"Deny rules por comando"," — bloqueia ",[26,2368,32],{},[26,2370,2371],{},"git push --force",[26,2373,2374],{},"git reset --hard",[2310,2376,2377,2380,2381],{},[48,2378,2379],{},"Hooks PreToolUse"," — scripts que leem o input da ferramenta via stdin, detectam secrets no conteúdo e bloqueiam comandos Bash que manipulam ",[26,2382,28],{},[2310,2384,2385,2387],{},[48,2386,73],{}," — regras semânticas que guiam o comportamento do Claude Code em tudo que ele escreve",[11,2389,2390,2391,2393],{},"Cada camada cobre o que a anterior não pega. Deny rules não analisam conteúdo. Hooks não definem comportamento. O ",[26,2392,73],{}," não tem enforcement binário. Juntas, cobrem praticamente todos os vetores.",[11,2395,2396],{},"Segurança não é só auditar o código que o Claude Code analisa. É configurar o ambiente pra que ele nunca tenha acesso ao que não precisa. Princípio do menor privilégio. Vale pra humanos, vale pra agentes de IA.",[2398,2399,2400],"style",{},"html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":107,"searchDepth":121,"depth":121,"links":2402},[2403,2404,2405,2406,2407,2408,2415,2416,2417,2418],{"id":40,"depth":121,"text":41},{"id":84,"depth":121,"text":85},{"id":270,"depth":121,"text":271},{"id":367,"depth":121,"text":368},{"id":471,"depth":121,"text":472},{"id":609,"depth":121,"text":610,"children":2409},[2410,2411,2412,2413,2414],{"id":753,"depth":131,"text":754},{"id":994,"depth":131,"text":995},{"id":1109,"depth":131,"text":1110},{"id":1243,"depth":131,"text":1244},{"id":1343,"depth":131,"text":1344},{"id":1651,"depth":121,"text":1652},{"id":2236,"depth":121,"text":2237},{"id":2301,"depth":121,"text":2302},{"id":2345,"depth":121,"text":2346},"2026-03-06","Permissions deny, hooks de validação, CLAUDE.md restritivo. Como configurar o Claude Code pra nunca ler seus .env, nunca expor secrets e nunca rodar comandos destrutivos sem aprovação.","md",{},"/blog/claude-code-seguranca-configuracoes-que-protegem-seu-codigo",{"title":6,"description":2420},"blog/claude-code-seguranca-configuracoes-que-protegem-seu-codigo",[2427,2428,2429,2430],"seguranca","claude-code","devtools","configuracao","E-h3KRR30mDyP4LivSd-uJjnWNbYEkabchWY6mInTzM",{"id":2433,"title":2434,"body":2435,"date":2687,"description":2688,"extension":2421,"meta":2689,"navigation":813,"path":2690,"seo":2691,"stem":2692,"tags":2693,"__hash__":2699},"blog/blog/promax-cnc-do-csv-ao-gcode.md","Promax CNC: do CSV ao G-code otimizado",{"type":8,"value":2436,"toc":2678},[2437,2440,2443,2447,2460,2473,2479,2482,2489,2493,2496,2499,2518,2522,2525,2528,2534,2538,2541,2544,2550,2553,2557,2560,2563,2569,2575,2581,2587,2590,2594,2597,2665,2668,2672,2675],[11,2438,2439],{},"Semana passada comecei a desenvolver a Promax CNC, uma nova empresa em sociedade com meu primo. A ideia é uma plataforma completa pra fábricas de corte CNC. Importa o desenho das peças, encaixa na chapa, otimiza o corte e gera o arquivo pronto pra máquina.",[11,2441,2442],{},"Vou explicar como cada etapa funciona por dentro.",[38,2444,2446],{"id":2445},"leitura-do-csv-de-peças","Leitura do CSV de peças",[11,2448,2449,2450,354,2453,354,2456,2459],{},"Marceneiros usam o Promob pra projetar móveis. O Promob exporta um CSV com a lista completa de peças do projeto. O parser da Promax detecta automaticamente o formato Promob pelas colunas (",[26,2451,2452],{},"PEÇA ID",[26,2454,2455],{},"OPERAÇÕES",[26,2457,2458],{},"BORDA FACE FRENTE",") e o delimitador (vírgula, ponto-e-vírgula ou pipe).",[11,2461,2462,2463,354,2466,354,2469,2472],{},"São mais de 30 colunas mapeadas: geometria (",[26,2464,2465],{},"ALTURA",[26,2467,2468],{},"PROF",[26,2470,2471],{},"ESPESSURA","), material, operações, bordas por face, sentido do veio da madeira, cliente, código do projeto, ambiente.",[11,2474,2475,2476,2478],{},"A parte mais interessante é o campo ",[26,2477,2455],{},". Dentro de uma célula CSV, vem um JSON com furos e rasgos definidos por coordenada, diâmetro, profundidade e face normal. A face normal indica em qual lado da peça fica o furo: topo, base, esquerda, direita, frente ou fundo.",[11,2480,2481],{},"O parser agrupa peças por material e espessura, calcula a área total em m² e estima quantas chapas serão necessárias. Itens comprados (dobradiças, corrediças) são separados automaticamente.",[11,2483,2484],{},[2485,2486],"img",{"alt":2487,"src":2488},"Resumo de materiais parseado do CSV Promob","/blog/csv-lista-pecas.png",[38,2490,2492],{"id":2491},"montagem-do-dxf","Montagem do DXF",[11,2494,2495],{},"O DXF é o formato padrão de desenho técnico. O parser lê o arquivo ASCII par a par (código + valor), extrai a seção ENTITIES e suporta POLYLINE, LWPOLYLINE, CIRCLE, LINE e ARC.",[11,2497,2498],{},"Polilíneas com bulge (curvatura) são convertidas em arcos. O valor de bulge é a tangente do ângulo incluso dividido por 4. Positivo é sentido anti-horário, negativo é horário.",[11,2500,2501,2502,2505,2506,2509,2510,2513,2514,2517],{},"O sistema de classificação de layers é inteligente. O nome do layer define a intenção: ",[26,2503,2504],{},"PECAS"," são perfis de corte, ",[26,2507,2508],{},"CHAPA"," é o contorno do material, ",[26,2511,2512],{},"FUROS_8_12"," significa furos de 8mm de diâmetro com 12mm de profundidade, ",[26,2515,2516],{},"SOBRAS"," são áreas de reaproveitamento. Tudo inferido pelo nome, sem metadados externos.",[38,2519,2521],{"id":2520},"visualização-2d","Visualização 2D",[11,2523,2524],{},"O preview 2D é SVG puro em React. A peça é escalada pro viewport com padding automático. Furos aparecem como círculos em ciano (filtrados pela face do topo), rasgos como retângulos em verde, e bordas como linhas âmbar em cada lado aplicado.",[11,2526,2527],{},"Leve e rápido. Serve pra validação rápida antes de mandar pro 3D.",[11,2529,2530],{},[2485,2531],{"alt":2532,"src":2533},"Nesting 2D com 17 peças encaixadas na chapa, 94.2% de aproveitamento","/blog/dfx-visualizacao-2d.png",[38,2535,2537],{"id":2536},"visualização-3d","Visualização 3D",[11,2539,2540],{},"O 3D usa Three.js com React Three Fiber. Cada tipo de movimento tem cor diferente: rápidos em vermelho (30% de opacidade), cortes em ciano, arcos em roxo, rampas em laranja, tabs em verde.",[11,2542,2543],{},"As coordenadas CNC (Z pra cima) são convertidas pro sistema do Three.js (Y pra cima). Um wireframe mostra o envelope de trabalho da máquina. OrbitControls pra rotacionar e um gizmo de orientação.",[11,2545,2546],{},[2485,2547],{"alt":2548,"src":2549},"Visualização 3D da chapa com extrusão da espessura real do material","/blog/dfx-visualizacao-3d.png",[11,2551,2552],{},"Também tem um motor de montagem de móveis. Ele classifica cada peça por função (lateral, topo, fundo, prateleira, porta, gaveta) usando pattern matching em 18 tipos de role. Com as dimensões inferidas dos painéis laterais, posiciona cada peça no espaço 3D e monta o móvel completo.",[38,2554,2556],{"id":2555},"nesting-encaixando-peças-na-chapa","Nesting: encaixando peças na chapa",[11,2558,2559],{},"O nesting é o coração da plataforma. O objetivo é encaixar o máximo de peças na chapa desperdiçando o mínimo de material.",[11,2561,2562],{},"Pra peças retangulares, o algoritmo MAXRECTS roda em 4 fases:",[11,2564,2565,2568],{},[48,2566,2567],{},"Fase 1",": 25 combinações determinísticas. 5 estratégias de ordenação (por área, altura, largura, perímetro, maior lado) multiplicadas por 5 heurísticas de posicionamento (menor sobra curta, menor sobra longa, menor área desperdiçada, canto inferior esquerdo, máximo contato com bordas).",[11,2570,2571,2574],{},[48,2572,2573],{},"Fase 2",": 3.000 permutações aleatórias com shuffle Fisher-Yates e RNG semeado. Explora o espaço de soluções que as heurísticas determinísticas não alcançam.",[11,2576,2577,2580],{},[48,2578,2579],{},"Fase 3",": Refinamento 2-opt nas 15 melhores soluções. Troca posições de duas peças e verifica se melhorou. Até 500 trocas por candidato.",[11,2582,2583,2586],{},[48,2584,2585],{},"Fase 4",": Seleciona a melhor solução pelo score: primeiro minimiza peças não encaixadas, depois minimiza chapas usadas, depois maximiza aproveitamento.",[11,2588,2589],{},"Pra peças com formas complexas (L, T, perfis irregulares), o sistema rasteriza o polígono numa grade de 10mm e usa um grid packer estilo Tetris. A peça é testada em 4 rotações (0, 90, 180, 270 graus) e posicionada pela heurística bottom-left.",[38,2591,2593],{"id":2592},"_11-otimizações-de-g-code","11 otimizações de G-code",[11,2595,2596],{},"Depois do nesting, o G-code passa por 11 passes de otimização em sequência:",[2307,2598,2599,2605,2611,2617,2623,2629,2635,2641,2647,2653,2659],{},[2310,2600,2601,2604],{},[48,2602,2603],{},"Remoção de colineares",". Pontos intermediários em linhas retas são eliminados. Reduz o tamanho do programa.",[2310,2606,2607,2610],{},[48,2608,2609],{},"Arc fitting",". Sequências de movimentos lineares (G1) que formam uma curva são substituídas por um único arco (G2/G3). O algoritmo calcula o círculo que passa por 3 pontos e verifica se todos os pontos intermediários estão dentro da tolerância.",[2310,2612,2613,2616],{},[48,2614,2615],{},"Eliminação de redundância",". Remove movimentos duplicados ou quase duplicados que softwares CAM costumam gerar.",[2310,2618,2619,2622],{},[48,2620,2621],{},"Ramp entry",". Em vez de descer perpendicular na peça (que força a ferramenta), faz uma rampa linear ou helicoidal. Aumenta a vida útil da ferramenta e melhora o acabamento.",[2310,2624,2625,2628],{},[48,2626,2627],{},"Lead in/out",". Adiciona arcos de aproximação e saída nos contornos. Evita marcas de ferramenta no ponto de entrada do corte.",[2310,2630,2631,2634],{},[48,2632,2633],{},"Tabs",". Identifica contornos fechados, calcula o perímetro e distribui tabs (pontes de material) uniformemente. Cada tab é uma sequência de 4 movimentos: sobe, avança, desce. Impede que a peça se solte durante o corte.",[2310,2636,2637,2640],{},[48,2638,2639],{},"Otimização de cantos",". Arredonda cantos agudos com raio configurável (padrão 3mm) e reduz o feed rate. Previne quebra de ferramenta e melhora o acabamento.",[2310,2642,2643,2646],{},[48,2644,2645],{},"Smart retract",". Quando o próximo corte está perto (menos de 50mm), reduz a altura de retração. Troca segurança por velocidade em layouts densos.",[2310,2648,2649,2652],{},[48,2650,2651],{},"Otimização de rápidos",". Remove posicionamentos rápidos intermediários desnecessários.",[2310,2654,2655,2658],{},[48,2656,2657],{},"Onion skin",". Em bolsões com múltiplos passes (zigzag/espiral), adapta a profundidade de aproximação nos passes subsequentes. Se o canal já foi cortado, não precisa descer com cautela.",[2310,2660,2661,2664],{},[48,2662,2663],{},"Ciclos de furação",". Converte séries de movimentos de mergulho em ciclos G81/G82/G83. Reduz drasticamente o tamanho do arquivo pra peças com muitos furos.",[11,2666,2667],{},"No final, um passe de validação verifica se os feed rates estão dentro dos limites da máquina e emite warnings pra movimentos suspeitos.",[38,2669,2671],{"id":2670},"o-resultado","O resultado",[11,2673,2674],{},"O pipeline completo: CSV do Promob entra, o sistema parseia as peças, gera os DXFs, encaixa na chapa, otimiza o G-code em 11 passes e entrega o arquivo .nc pronto pra máquina. O que antes era manual e demorado agora é automatizado.",[11,2676,2677],{},"A Promax ainda tá em desenvolvimento. Falta fechar o catálogo de materiais e o módulo de orçamento. Mas o core funciona e os resultados de nesting e otimização já são consistentes.",{"title":107,"searchDepth":121,"depth":121,"links":2679},[2680,2681,2682,2683,2684,2685,2686],{"id":2445,"depth":121,"text":2446},{"id":2491,"depth":121,"text":2492},{"id":2520,"depth":121,"text":2521},{"id":2536,"depth":121,"text":2537},{"id":2555,"depth":121,"text":2556},{"id":2592,"depth":121,"text":2593},{"id":2670,"depth":121,"text":2671},"2026-03-05","Como funciona a leitura de peças, montagem de DXF, visualização 2D/3D e as 11 otimizações de G-code da plataforma que estou construindo.",{},"/blog/promax-cnc-do-csv-ao-gcode",{"title":2434,"description":2688},"blog/promax-cnc-do-csv-ao-gcode",[2694,2695,2696,2697,2698],"promax","cnc","gcode","nesting","three.js","dr5cJ0cVWeQIPO3aRt8_GMh8VCoaRCyepRKsrCe1jlw",{"id":2701,"title":2702,"body":2703,"date":4243,"description":4244,"extension":2421,"meta":4245,"navigation":813,"path":4246,"seo":4247,"stem":4248,"tags":4249,"__hash__":4252},"blog/blog/claude-code-security-ja-estava-disponivel.md","O Claude Code Security já estava disponível, você só não usava",{"type":8,"value":2704,"toc":4207},[2705,2708,2711,2715,2718,2725,2728,2732,2739,2750,2761,2764,2768,2783,2786,2792,2798,2801,2831,2838,2845,2848,2937,2940,2947,2954,3034,3041,3048,3051,3112,3119,3122,3129,3138,3142,3145,3149,3160,3163,3306,3313,3326,3330,3340,3500,3519,3523,3526,3738,3742,3745,3748,3759,3766,3788,3795,3799,3802,3829,3835,3841,3845,3851,3877,3880,3884,3890,3894,3899,3936,3939,3943,3947,3963,3967,3974,3988,3994,4060,4067,4071,4077,4109,4112,4116,4119,4127,4130,4136,4140,4146,4164,4175,4184,4187,4191,4194,4201,4204],[11,2706,2707],{},"Semana passada a Anthropic anunciou que vai lançar uma ferramenta de segurança integrada ao Claude Code. O mercado reagiu como se fosse uma revolução. Empresas de segurança perderam bilhões em valor de mercado.",[11,2709,2710],{},"Mas a verdade é que o Claude Code Security já estava disponível. Você só não usava.",[38,2712,2714],{"id":2713},"ferramentas-de-segurança-existem-há-décadas","Ferramentas de segurança existem há décadas",[11,2716,2717],{},"SonarQube, Snyk, Checkmarx, Fortify, OWASP ZAP, Burp Suite, Veracode, Semgrep. A lista é longa. Ferramentas de análise estática, análise dinâmica, scanners de dependência, pentest automatizado. Não falta ferramenta.",[11,2719,2720,2721,2724],{},"Todas funcionam da mesma forma: olham o código ",[48,2722,2723],{},"de fora",". São scanners. Recebem seu código como input, rodam regras, cospem um relatório. Você lê o PDF, prioriza os findings, abre tickets, e talvez corrija alguns. Meses depois, roda de novo.",[11,2726,2727],{},"O modelo mental é: código de um lado, segurança do outro. Dois mundos separados que se encontram no CI/CD.",[38,2729,2731],{"id":2730},"a-mudança-real-segurança-de-dentro-do-código","A mudança real: segurança de dentro do código",[11,2733,2734,2735,2738],{},"O que muda com o Claude Code não é \"mais uma ferramenta de segurança\". É o ",[48,2736,2737],{},"lugar"," de onde a segurança acontece.",[11,2740,2741,2742,2745,2746,2749],{},"Quando o Claude Code audita seu código, ele não está rodando regras de regex por cima dos seus arquivos. Ele está ",[48,2743,2744],{},"lendo"," o código. Entendendo o fluxo de autenticação. Seguindo a cadeia de chamadas do handler até o banco de dados. Verificando se o rate limit usa Redis (distribuído) ou memória (inútil em múltiplas instâncias). Olhando se a projeção do MongoDB está filtrando os campos sensíveis. Checando se o ",[26,2747,2748],{},"timingSafeEqual"," está sendo usado na comparação de tokens.",[11,2751,2752,2753,2756,2757,2760],{},"Ele entende ",[48,2754,2755],{},"contexto",". Sabe que aquele ",[26,2758,2759],{},"findById(req.params.id)"," sem filtro de tenant é um BOLA (Broken Object Level Authorization), o vulnerability #1 do OWASP API Security Top 10. Não porque bateu uma regex, mas porque entendeu que a query não tem escopo de multi-tenancy.",[11,2762,2763],{},"É a diferença entre um guarda que olha o prédio pelo lado de fora e um que conhece cada sala por dentro.",[38,2765,2767],{"id":2766},"eu-já-fazia-isso-com-skills","Eu já fazia isso. Com skills.",[11,2769,2770,2771,2774,2775,2778,2779,2782],{},"O Claude Code tem um recurso chamado ",[48,2772,2773],{},"skills",", arquivos markdown em ",[26,2776,2777],{},"~/.claude/skills/"," que funcionam como instruções especializadas. Quando você invoca uma skill (tipo ",[26,2780,2781],{},"/security-scan","), o Claude Code recebe aquele contexto e executa a tarefa com o conhecimento embutido no arquivo.",[11,2784,2785],{},"Eu criei 6 skills de segurança que transformaram meu Claude Code num auditor completo:",[751,2787,2789,2791],{"id":2788},"security-scan-owasp-soc-2",[26,2790,2781],{},": OWASP + SOC 2",[11,2793,2794,2795,2797],{},"A skill mais usada no dia a dia. Toda vez que termino uma feature, rodo ",[26,2796,2781],{}," nos arquivos alterados.",[11,2799,2800],{},"Ela faz:",[2307,2802,2803,2809,2812,2815,2818,2821],{},[2310,2804,2805,2806],{},"Identifica os arquivos alterados via ",[26,2807,2808],{},"git diff",[2310,2810,2811],{},"Detecta o framework (Fastify, no meu caso)",[2310,2813,2814],{},"Lê cada arquivo procurando as 7 categorias do OWASP Top 10: Injection, Broken Auth, Sensitive Data Exposure, Broken Access Control, Security Misconfiguration, XSS, Insecure Dependencies",[2310,2816,2817],{},"Cruza com critérios SOC 2: CC6 (Access Control), CC7 (System Operations), PI (Processing Integrity), C (Confidentiality), A (Availability)",[2310,2819,2820],{},"Aplica regras específicas de stack: Node.js/Fastify, MongoDB/Mongoose, Redis, Cryptografia",[2310,2822,2823,2826,2827,2830],{},[48,2824,2825],{},"Cruza com a documentação de segurança do projeto",": se existe um ",[26,2828,2829],{},"AUDIT_2026.md"," com vulnerabilidades já corrigidas, verifica se a mudança não reintroduz o problema",[11,2832,2833,2834,2837],{},"O relatório sai com severidade (CRITICAL/HIGH/MEDIUM/LOW), localização exata (",[26,2835,2836],{},"file:line","), categoria OWASP ou critério SOC 2, o risco, e o fix exato.",[751,2839,2841,2844],{"id":2840},"security-hardening-auditoria-profunda-de-produção",[26,2842,2843],{},"/security-hardening",": Auditoria profunda de produção",[11,2846,2847],{},"Essa é a skill pesada. Vai muito além de SOC 2. Cobre:",[2849,2850,2851,2857,2863,2882,2897,2907,2913,2919,2925,2931],"ul",{},[2310,2852,2853,2856],{},[48,2854,2855],{},"OWASP Top 10 (2025)",": todos os 10 itens com patterns de grep e exemplos de código vulnerável vs seguro",[2310,2858,2859,2862],{},[48,2860,2861],{},"OWASP API Security Top 10",": BOLA, Broken Authentication, Mass Assignment, SSRF, cada um com checklist",[2310,2864,2865,2868,2869,354,2872,354,2875,354,2878,2881],{},[48,2866,2867],{},"Prevenção de NoSQL Injection",": patterns específicos de MongoDB (",[26,2870,2871],{},"$gt",[26,2873,2874],{},"$ne",[26,2876,2877],{},"$regex",[26,2879,2880],{},"$where",")",[2310,2883,2884,672,2887,354,2890,354,2893,2896],{},[48,2885,2886],{},"Prototype Pollution",[26,2888,2889],{},"__proto__",[26,2891,2892],{},"constructor",[26,2894,2895],{},"prototype"," rejeitados no input",[2310,2898,2899,2902,2903,2906],{},[48,2900,2901],{},"Rate Limiting & DDoS",": multi-tier limits, Redis-backed, ",[26,2904,2905],{},"Retry-After",", Slowloris protection",[2310,2908,2909,2912],{},[48,2910,2911],{},"Zero Trust",": re-validação de sessão, encrypt everything, log every access decision",[2310,2914,2915,2918],{},[48,2916,2917],{},"LGPD/GDPR",": minimização de dados, projeções MongoDB, direito de acesso/exclusão/portabilidade",[2310,2920,2921,2924],{},[48,2922,2923],{},"Secrets Management",": patterns de grep para encontrar secrets hardcoded",[2310,2926,2927,2930],{},[48,2928,2929],{},"Security Headers",": HSTS, CSP, X-Frame-Options, COOP, CORP",[2310,2932,2933,2936],{},[48,2934,2935],{},"Penetration Testing Checklist",": checklist manual para testes trimestrais",[11,2938,2939],{},"A skill termina com um score de 0 a 100, mapa de superfície de ataque, e roadmap de remediação priorizado.",[751,2941,2943,2946],{"id":2942},"soc2-audit-soc-2-type-i-e-type-ii-completo",[26,2944,2945],{},"/soc2-audit",": SOC 2 Type I e Type II completo",[11,2948,2949,2950,2953],{},"Essa cobre os ",[48,2951,2952],{},"61 critérios"," do Trust Services Criteria (TSC) do AICPA:",[2849,2955,2956,2962,2968,2974,2980,2986,2992,2998,3004,3010,3016,3022,3028],{},[2310,2957,2958,2961],{},[48,2959,2960],{},"CC1",": Ambiente de Controle (integridade, supervisão, estrutura, competência, accountability)",[2310,2963,2964,2967],{},[48,2965,2966],{},"CC2",": Comunicação e Informação (qualidade da informação, comunicação interna/externa)",[2310,2969,2970,2973],{},[48,2971,2972],{},"CC3",": Avaliação de Risco (objetivos, análise de risco, fraude, gestão de mudanças)",[2310,2975,2976,2979],{},[48,2977,2978],{},"CC4",": Monitoramento (APM, health checks, alertas, error tracking)",[2310,2981,2982,2985],{},[48,2983,2984],{},"CC5",": Atividades de Controle (auth middleware, sanitização, WAF, CI/CD)",[2310,2987,2988,2991],{},[48,2989,2990],{},"CC6",": Controles de Acesso (autenticação, registro, remoção, restrições físicas, multi-tenancy)",[2310,2993,2994,2997],{},[48,2995,2996],{},"CC7",": Operações do Sistema (monitoring, anomaly detection, incident response, recovery)",[2310,2999,3000,3003],{},[48,3001,3002],{},"CC8",": Gestão de Mudanças (version control, branch protection, PR reviews, CI)",[2310,3005,3006,3009],{},[48,3007,3008],{},"CC9",": Mitigação de Riscos (vendor risk, webhook signatures, SLA monitoring)",[2310,3011,3012,3015],{},[48,3013,3014],{},"A1",": Disponibilidade (capacity, health checks, backups, DR)",[2310,3017,3018,3021],{},[48,3019,3020],{},"PI1",": Integridade de Processamento (validação, idempotência, reconciliação, error correction)",[2310,3023,3024,3027],{},[48,3025,3026],{},"C1",": Confidencialidade (classificação de dados, disposal)",[2310,3029,3030,3033],{},[48,3031,3032],{},"P1",": Privacidade (notice, consent, collection, retention, access, disclosure, quality, monitoring)",[11,3035,3036,3037,3040],{},"O diferencial de Type II: não basta o controle existir no código. A skill verifica se existe ",[48,3038,3039],{},"evidência de operação contínua",": logs provando que o controle roda consistentemente, enforcement automatizado, monitoramento que pega falhas.",[751,3042,3044,3047],{"id":3043},"query-review-review-específico-de-mongodb",[26,3045,3046],{},"/query-review",": Review específico de MongoDB",[11,3049,3050],{},"Toda query MongoDB que eu escrevo passa por essa skill. Ela verifica:",[2849,3052,3053,3059,3065,3079,3091,3099],{},[2310,3054,3055,3058],{},[48,3056,3057],{},"Projeção obrigatória",": nunca retornar documentos inteiros (stores podem ter 50KB+)",[2310,3060,3061,3064],{},[48,3062,3063],{},"Timezone",": datas no MongoDB são UTC, Brasil é UTC-3, meia-noite BRT = 03:00 UTC",[2310,3066,3067,3070,3071,3074,3075,3078],{},[48,3068,3069],{},"Índices faltantes",": fields em ",[26,3072,3073],{},"$match"," depois de ",[26,3076,3077],{},"$unwind",", sorts em grandes collections",[2310,3080,3081,3087,3088],{},[48,3082,3083,3086],{},[26,3084,3085],{},"lean()"," faltando",": queries read-only devem usar ",[26,3089,3090],{},".lean()",[2310,3092,3093,3098],{},[48,3094,3095,3086],{},[26,3096,3097],{},"await",": Mongoose queries são thenables, sem await = bug silencioso",[2310,3100,3101,672,3104,3107,3108,3111],{},[48,3102,3103],{},"Queries sem limite",[26,3105,3106],{},"find()"," sem ",[26,3109,3110],{},"limit()"," em collections grandes",[751,3113,3115,3118],{"id":3114},"severity-review-review-com-profundidade-configurável",[26,3116,3117],{},"/severity-review",": Review com profundidade configurável",[11,3120,3121],{},"Três modos: Critical Only (pré-merge rápido), Standard (review normal), Thorough (nitpick completo). O dev escolhe a profundidade.",[751,3123,3125,3128],{"id":3124},"docs-documentação-automática-de-segurança",[26,3126,3127],{},"/docs",": Documentação automática de segurança",[11,3130,3131,3132,96,3134,3137],{},"Depois de qualquer mudança, essa skill detecta o que mudou e atualiza os docs correspondentes, incluindo ",[26,3133,2829],{},[26,3135,3136],{},"SOC2_COMPLIANCE.md"," com contadores, status, e barras de progresso.",[38,3139,3141],{"id":3140},"o-que-isso-produziu-na-prática","O que isso produziu na prática",[11,3143,3144],{},"Agora vamos aos exemplos. Simulando em códigos legados, o resultado foi:",[751,3146,3148],{"id":3147},"nosql-injection-seu-login-aceita-operadores-do-mongodb","NoSQL Injection: seu login aceita operadores do MongoDB?",[11,3150,3151,3152,3155,3156,3159],{},"Esse é o clássico. Um código de login que faz ",[26,3153,3154],{},"findOne({ email, password })"," direto no banco sem validar tipo. Se alguém mandar ",[26,3157,3158],{},"{ \"$gt\": \"\" }"," no campo password, o MongoDB retorna o primeiro documento que corresponde. Login sem senha.",[11,3161,3162],{},"O que a skill encontra e o que o teste valida:",[102,3164,3168],{"className":3165,"code":3166,"language":3167,"meta":107,"style":107},"language-typescript shiki shiki-themes github-light github-dark","// O atacante manda isso no body:\n// { email: { $gt: '' }, password: 'anything' }\n// Se o backend não validar tipo, o MongoDB interpreta $gt como operador\n\nit('rejects $gt operator in email', async () => {\n  const res = await app.inject({\n    method: 'POST',\n    url: '/login',\n    payload: { email: { $gt: '' }, password: 'anything' },\n  })\n  expect(res.statusCode).toBe(400) // Zod rejeita: email tem que ser string\n})\n","typescript",[26,3169,3170,3175,3180,3185,3189,3214,3237,3247,3257,3274,3279,3301],{"__ignoreMap":107},[111,3171,3172],{"class":113,"line":114},[111,3173,3174],{"class":766},"// O atacante manda isso no body:\n",[111,3176,3177],{"class":113,"line":121},[111,3178,3179],{"class":766},"// { email: { $gt: '' }, password: 'anything' }\n",[111,3181,3182],{"class":113,"line":131},[111,3183,3184],{"class":766},"// Se o backend não validar tipo, o MongoDB interpreta $gt como operador\n",[111,3186,3187],{"class":113,"line":140},[111,3188,814],{"emptyLinePlaceholder":813},[111,3190,3191,3194,3197,3200,3202,3205,3208,3211],{"class":113,"line":150},[111,3192,3193],{"class":792},"it",[111,3195,3196],{"class":117},"(",[111,3198,3199],{"class":143},"'rejects $gt operator in email'",[111,3201,354],{"class":117},[111,3203,3204],{"class":785},"async",[111,3206,3207],{"class":117}," () ",[111,3209,3210],{"class":785},"=>",[111,3212,3213],{"class":117}," {\n",[111,3215,3216,3219,3222,3225,3228,3231,3234],{"class":113,"line":158},[111,3217,3218],{"class":785},"  const",[111,3220,3221],{"class":124}," res",[111,3223,3224],{"class":785}," =",[111,3226,3227],{"class":785}," await",[111,3229,3230],{"class":117}," app.",[111,3232,3233],{"class":792},"inject",[111,3235,3236],{"class":117},"({\n",[111,3238,3239,3242,3245],{"class":113,"line":166},[111,3240,3241],{"class":117},"    method: ",[111,3243,3244],{"class":143},"'POST'",[111,3246,147],{"class":117},[111,3248,3249,3252,3255],{"class":113,"line":174},[111,3250,3251],{"class":117},"    url: ",[111,3253,3254],{"class":143},"'/login'",[111,3256,147],{"class":117},[111,3258,3259,3262,3265,3268,3271],{"class":113,"line":182},[111,3260,3261],{"class":117},"    payload: { email: { $gt: ",[111,3263,3264],{"class":143},"''",[111,3266,3267],{"class":117}," }, password: ",[111,3269,3270],{"class":143},"'anything'",[111,3272,3273],{"class":117}," },\n",[111,3275,3276],{"class":113,"line":188},[111,3277,3278],{"class":117},"  })\n",[111,3280,3281,3284,3287,3290,3292,3295,3298],{"class":113,"line":194},[111,3282,3283],{"class":792},"  expect",[111,3285,3286],{"class":117},"(res.statusCode).",[111,3288,3289],{"class":792},"toBe",[111,3291,3196],{"class":117},[111,3293,3294],{"class":124},"400",[111,3296,3297],{"class":117},") ",[111,3299,3300],{"class":766},"// Zod rejeita: email tem que ser string\n",[111,3302,3303],{"class":113,"line":202},[111,3304,3305],{"class":117},"})\n",[11,3307,3308,3309,3312],{},"O fix é simples: Zod na entrada. ",[26,3310,3311],{},"z.string().email()"," mata o problema na raiz. Mas sem a skill apontando, quantas APIs em produção hoje aceitam objetos onde deveria ter string?",[11,3314,3315,3316,354,3318,354,3320,354,3323,3325],{},"A mesma lógica vale pra ",[26,3317,2874],{},[26,3319,2877],{},[26,3321,3322],{},"$or",[26,3324,2880],{},". Cada operador é um vetor de ataque. A skill testa todos, em todos os endpoints de autenticação.",[751,3327,3329],{"id":3328},"prototype-pollution-o-json-que-envenena-o-processo-inteiro","Prototype Pollution: o JSON que envenena o processo inteiro",[11,3331,3332,3333,3335,3336,3339],{},"Esse é mais sutil. O atacante manda um JSON com ",[26,3334,2889],{}," no body. Se o backend usar ",[26,3337,3338],{},"Object.assign()"," ou spread sem sanitizar, o prototype de todos os objetos do processo é poluído.",[102,3341,3343],{"className":3165,"code":3342,"language":3167,"meta":107,"style":107},"// O atacante manda JSON raw com __proto__\n// Se o parser não rejeitar, {\"isAdmin\": true} vaza pro Object.prototype\n// E qualquer {} no processo passa a ter isAdmin === true\n\nit('rejects __proto__ field with 400', async () => {\n  const res = await app.inject({\n    method: 'POST',\n    url: '/login',\n    headers: { 'content-type': 'application/json' },\n    payload: '{\"email\":\"real@test.com\",\"password\":\"correct123\",\"__proto__\":{\"isAdmin\":true}}',\n  })\n  expect(res.statusCode).toBe(400)\n  // Verifica que Object.prototype NÃO foi poluído\n  expect(({} as Record\u003Cstring, unknown>).isAdmin).toBeUndefined()\n})\n",[26,3344,3345,3350,3355,3360,3364,3383,3399,3407,3415,3430,3440,3444,3458,3463,3496],{"__ignoreMap":107},[111,3346,3347],{"class":113,"line":114},[111,3348,3349],{"class":766},"// O atacante manda JSON raw com __proto__\n",[111,3351,3352],{"class":113,"line":121},[111,3353,3354],{"class":766},"// Se o parser não rejeitar, {\"isAdmin\": true} vaza pro Object.prototype\n",[111,3356,3357],{"class":113,"line":131},[111,3358,3359],{"class":766},"// E qualquer {} no processo passa a ter isAdmin === true\n",[111,3361,3362],{"class":113,"line":140},[111,3363,814],{"emptyLinePlaceholder":813},[111,3365,3366,3368,3370,3373,3375,3377,3379,3381],{"class":113,"line":150},[111,3367,3193],{"class":792},[111,3369,3196],{"class":117},[111,3371,3372],{"class":143},"'rejects __proto__ field with 400'",[111,3374,354],{"class":117},[111,3376,3204],{"class":785},[111,3378,3207],{"class":117},[111,3380,3210],{"class":785},[111,3382,3213],{"class":117},[111,3384,3385,3387,3389,3391,3393,3395,3397],{"class":113,"line":158},[111,3386,3218],{"class":785},[111,3388,3221],{"class":124},[111,3390,3224],{"class":785},[111,3392,3227],{"class":785},[111,3394,3230],{"class":117},[111,3396,3233],{"class":792},[111,3398,3236],{"class":117},[111,3400,3401,3403,3405],{"class":113,"line":166},[111,3402,3241],{"class":117},[111,3404,3244],{"class":143},[111,3406,147],{"class":117},[111,3408,3409,3411,3413],{"class":113,"line":174},[111,3410,3251],{"class":117},[111,3412,3254],{"class":143},[111,3414,147],{"class":117},[111,3416,3417,3420,3423,3425,3428],{"class":113,"line":182},[111,3418,3419],{"class":117},"    headers: { ",[111,3421,3422],{"class":143},"'content-type'",[111,3424,672],{"class":117},[111,3426,3427],{"class":143},"'application/json'",[111,3429,3273],{"class":117},[111,3431,3432,3435,3438],{"class":113,"line":188},[111,3433,3434],{"class":117},"    payload: ",[111,3436,3437],{"class":143},"'{\"email\":\"real@test.com\",\"password\":\"correct123\",\"__proto__\":{\"isAdmin\":true}}'",[111,3439,147],{"class":117},[111,3441,3442],{"class":113,"line":194},[111,3443,3278],{"class":117},[111,3445,3446,3448,3450,3452,3454,3456],{"class":113,"line":202},[111,3447,3283],{"class":792},[111,3449,3286],{"class":117},[111,3451,3289],{"class":792},[111,3453,3196],{"class":117},[111,3455,3294],{"class":124},[111,3457,808],{"class":117},[111,3459,3460],{"class":113,"line":210},[111,3461,3462],{"class":766},"  // Verifica que Object.prototype NÃO foi poluído\n",[111,3464,3465,3467,3470,3473,3476,3479,3482,3484,3487,3490,3493],{"class":113,"line":218},[111,3466,3283],{"class":792},[111,3468,3469],{"class":117},"(({} ",[111,3471,3472],{"class":785},"as",[111,3474,3475],{"class":792}," Record",[111,3477,3478],{"class":117},"\u003C",[111,3480,3481],{"class":124},"string",[111,3483,354],{"class":117},[111,3485,3486],{"class":124},"unknown",[111,3488,3489],{"class":117},">).isAdmin).",[111,3491,3492],{"class":792},"toBeUndefined",[111,3494,3495],{"class":117},"()\n",[111,3497,3498],{"class":113,"line":226},[111,3499,3305],{"class":117},[11,3501,3502,3503,3506,3507,3510,3511,3514,3515,3518],{},"A última linha é a mais importante: ela verifica que criar um objeto vazio ",[26,3504,3505],{},"{}"," e acessar ",[26,3508,3509],{},".isAdmin"," retorna ",[26,3512,3513],{},"undefined",". Se retornasse ",[26,3516,3517],{},"true",", o processo inteiro estaria comprometido.",[751,3520,3522],{"id":3521},"response-leakage-usuário-não-encontrado-vs-senha-incorreta","Response Leakage: \"usuário não encontrado\" vs \"senha incorreta\"",[11,3524,3525],{},"Se sua API retorna mensagens diferentes pra email inexistente e senha errada, qualquer atacante consegue enumerar quais emails existem no seu sistema. Com uma lista de emails, o ataque de força bruta fica muito mais eficiente.",[102,3527,3529],{"className":3165,"code":3528,"language":3167,"meta":107,"style":107},"it('error messages do not reveal whether email exists', async () => {\n  const resExists = await app.inject({\n    method: 'POST', url: '/login',\n    payload: { email: 'real@test.com', password: 'wrongpassword' },\n  })\n  const resNotExists = await app.inject({\n    method: 'POST', url: '/login',\n    payload: { email: 'notexist@test.com', password: 'wrongpassword' },\n  })\n  // Status code idêntico\n  expect(resExists.statusCode).toBe(401)\n  expect(resNotExists.statusCode).toBe(401)\n  // Mensagem idêntica: não dá pra saber qual email existe\n  expect(resExists.json().message).toBe(resNotExists.json().message)\n  // Até a messageKey tem que ser igual\n  expect(resExists.json().messageKey).toBe(resNotExists.json().messageKey)\n})\n",[26,3530,3531,3550,3567,3580,3596,3600,3617,3629,3642,3646,3651,3667,3682,3687,3709,3714,3734],{"__ignoreMap":107},[111,3532,3533,3535,3537,3540,3542,3544,3546,3548],{"class":113,"line":114},[111,3534,3193],{"class":792},[111,3536,3196],{"class":117},[111,3538,3539],{"class":143},"'error messages do not reveal whether email exists'",[111,3541,354],{"class":117},[111,3543,3204],{"class":785},[111,3545,3207],{"class":117},[111,3547,3210],{"class":785},[111,3549,3213],{"class":117},[111,3551,3552,3554,3557,3559,3561,3563,3565],{"class":113,"line":121},[111,3553,3218],{"class":785},[111,3555,3556],{"class":124}," resExists",[111,3558,3224],{"class":785},[111,3560,3227],{"class":785},[111,3562,3230],{"class":117},[111,3564,3233],{"class":792},[111,3566,3236],{"class":117},[111,3568,3569,3571,3573,3576,3578],{"class":113,"line":131},[111,3570,3241],{"class":117},[111,3572,3244],{"class":143},[111,3574,3575],{"class":117},", url: ",[111,3577,3254],{"class":143},[111,3579,147],{"class":117},[111,3581,3582,3585,3588,3591,3594],{"class":113,"line":140},[111,3583,3584],{"class":117},"    payload: { email: ",[111,3586,3587],{"class":143},"'real@test.com'",[111,3589,3590],{"class":117},", password: ",[111,3592,3593],{"class":143},"'wrongpassword'",[111,3595,3273],{"class":117},[111,3597,3598],{"class":113,"line":150},[111,3599,3278],{"class":117},[111,3601,3602,3604,3607,3609,3611,3613,3615],{"class":113,"line":158},[111,3603,3218],{"class":785},[111,3605,3606],{"class":124}," resNotExists",[111,3608,3224],{"class":785},[111,3610,3227],{"class":785},[111,3612,3230],{"class":117},[111,3614,3233],{"class":792},[111,3616,3236],{"class":117},[111,3618,3619,3621,3623,3625,3627],{"class":113,"line":166},[111,3620,3241],{"class":117},[111,3622,3244],{"class":143},[111,3624,3575],{"class":117},[111,3626,3254],{"class":143},[111,3628,147],{"class":117},[111,3630,3631,3633,3636,3638,3640],{"class":113,"line":174},[111,3632,3584],{"class":117},[111,3634,3635],{"class":143},"'notexist@test.com'",[111,3637,3590],{"class":117},[111,3639,3593],{"class":143},[111,3641,3273],{"class":117},[111,3643,3644],{"class":113,"line":182},[111,3645,3278],{"class":117},[111,3647,3648],{"class":113,"line":188},[111,3649,3650],{"class":766},"  // Status code idêntico\n",[111,3652,3653,3655,3658,3660,3662,3665],{"class":113,"line":194},[111,3654,3283],{"class":792},[111,3656,3657],{"class":117},"(resExists.statusCode).",[111,3659,3289],{"class":792},[111,3661,3196],{"class":117},[111,3663,3664],{"class":124},"401",[111,3666,808],{"class":117},[111,3668,3669,3671,3674,3676,3678,3680],{"class":113,"line":202},[111,3670,3283],{"class":792},[111,3672,3673],{"class":117},"(resNotExists.statusCode).",[111,3675,3289],{"class":792},[111,3677,3196],{"class":117},[111,3679,3664],{"class":124},[111,3681,808],{"class":117},[111,3683,3684],{"class":113,"line":210},[111,3685,3686],{"class":766},"  // Mensagem idêntica: não dá pra saber qual email existe\n",[111,3688,3689,3691,3694,3696,3699,3701,3704,3706],{"class":113,"line":218},[111,3690,3283],{"class":792},[111,3692,3693],{"class":117},"(resExists.",[111,3695,106],{"class":792},[111,3697,3698],{"class":117},"().message).",[111,3700,3289],{"class":792},[111,3702,3703],{"class":117},"(resNotExists.",[111,3705,106],{"class":792},[111,3707,3708],{"class":117},"().message)\n",[111,3710,3711],{"class":113,"line":226},[111,3712,3713],{"class":766},"  // Até a messageKey tem que ser igual\n",[111,3715,3716,3718,3720,3722,3725,3727,3729,3731],{"class":113,"line":232},[111,3717,3283],{"class":792},[111,3719,3693],{"class":117},[111,3721,106],{"class":792},[111,3723,3724],{"class":117},"().messageKey).",[111,3726,3289],{"class":792},[111,3728,3703],{"class":117},[111,3730,106],{"class":792},[111,3732,3733],{"class":117},"().messageKey)\n",[111,3735,3736],{"class":113,"line":238},[111,3737,3305],{"class":117},[751,3739,3741],{"id":3740},"_2fa-bypass-a-vulnerabilidade-que-nenhum-scanner-encontra","2FA Bypass: a vulnerabilidade que nenhum scanner encontra",[11,3743,3744],{},"Essa foi a mais grave que a skill encontrou. É uma vulnerabilidade de lógica de negócio, não de input.",[11,3746,3747],{},"O fluxo de login era:",[2307,3749,3750,3753,3756],{},[2310,3751,3752],{},"Usuário manda email + password corretos",[2310,3754,3755],{},"Se tem 2FA, a API pede o código TOTP",[2310,3757,3758],{},"Usuário manda email + password + totp_code",[11,3760,3761,3762,3765],{},"O problema: no passo 1, quando a API confirmava email + password e pedia o TOTP, ela ",[48,3763,3764],{},"resetava o contador de tentativas falhas",". Então o atacante podia:",[2307,3767,3768,3774,3780,3785],{},[2310,3769,3770,3773],{},[26,3771,3772],{},"{email, password}"," → \"informe o TOTP\", counter = 0",[2310,3775,3776,3779],{},[26,3777,3778],{},"{email, password, totp_code: \"000001\"}"," → falhou, counter = 1",[2310,3781,3782,3784],{},[26,3783,3772],{}," → \"informe o TOTP\", counter = 0 (reset!)",[2310,3786,3787],{},"Repetir até acertar",[11,3789,3790,3791,3794],{},"Com 1 milhão de combinações possíveis no TOTP de 6 dígitos, e o counter sempre voltando a zero, era questão de tempo. Nenhum scanner de regex ou análise estática encontra isso. É preciso ",[48,3792,3793],{},"entender o fluxo"," e perceber que a ordem das operações está errada.",[751,3796,3798],{"id":3797},"mais-exemplos-do-que-a-skill-pega","Mais exemplos do que a skill pega",[11,3800,3801],{},"A suíte completa cobre:",[11,3803,3804,3807,3808,354,3810,354,3812,354,3814,354,3816,3818,3819,354,3821,3824,3825,3828],{},[48,3805,3806],{},"injection.test.ts",": NoSQL injection (",[26,3809,2871],{},[26,3811,2874],{},[26,3813,2877],{},[26,3815,2880],{},[26,3817,3322],{},"), prototype pollution (",[26,3820,2889],{},[26,3822,3823],{},"constructor.prototype","), XSS (script tags, img onerror), command injection (",[26,3826,3827],{},"$(whoami)","), type confusion (number, boolean, array, null onde espera string), null byte injection, header injection, response leakage, oversized payloads, malformed JSON.",[11,3830,3831,3834],{},[48,3832,3833],{},"routes.test.ts",": HTTP verb tampering, path traversal, session fixation, IDOR, mass assignment, 2FA bypass.",[11,3836,3837,3840],{},[48,3838,3839],{},"headers-cors.test.ts",": CORS origin validation, Helmet headers, cookie security, Content-Type enforcement.",[751,3842,3844],{"id":3843},"_38-vulnerabilidades-catalogadas-no-total","38 vulnerabilidades catalogadas no total",[11,3846,3847,3848,3850],{},"Tudo documentado no ",[26,3849,2829],{},":",[2849,3852,3853,3859,3865,3871],{},[2310,3854,3855,3858],{},[48,3856,3857],{},"2 CRITICAL",": password reset link logado em plaintext, 2FA bypass via rate limit reset",[2310,3860,3861,3864],{},[48,3862,3863],{},"9 HIGH",": admin token com MD5, session token sem entropia, swagger exposto em produção",[2310,3866,3867,3870],{},[48,3868,3869],{},"13 MEDIUM",": AES-CTR sem autenticação, rate limit desabilitado quando Redis cai, CORS regex aceita domínio errado",[2310,3872,3873,3876],{},[48,3874,3875],{},"14 LOW",": console.log em produção, backup codes sem timing-safe comparison",[11,3878,3879],{},"Com plano de remediação em 4 fases. Fase 1 (críticos): concluída no mesmo dia. Fase 2 (high): 7 de 8 feitos.",[751,3881,3883],{"id":3882},"_164-testes-de-segurança-no-total","164 testes de segurança no total",[11,3885,3886,3887,100],{},"Tudo que foi mostrado acima são exemplos reais da suíte. No total, são 164 testes: 75 funcionais + 89 de segurança, distribuídos em 4 arquivos dedicados em ",[26,3888,3889],{},"tests/security/",[751,3891,3893],{"id":3892},"soc-2-compliance-checklist-com-157-itens","SOC 2 Compliance Checklist com 157 itens",[11,3895,3896,3897,3850],{},"Rastreamento completo no ",[26,3898,3136],{},[2849,3900,3901,3908,3915,3922,3929],{},[2310,3902,3903,3904,3907],{},"Autenticação: ",[48,3905,3906],{},"100%"," (20/20 itens)",[2310,3909,3910,3911,3914],{},"Rate Limiting: ",[48,3912,3913],{},"63%"," (5/8)",[2310,3916,3917,3918,3921],{},"Sessões e Tokens: ",[48,3919,3920],{},"60%"," (6/10)",[2310,3923,3924,3925,3928],{},"Proteção de Dados: ",[48,3926,3927],{},"50%"," (6/12)",[2310,3930,3931,3932,3935],{},"Logging e Auditoria: ",[48,3933,3934],{},"38%"," (6/16)",[11,3937,3938],{},"Com progresso visual e plano de ação de 39 itens priorizados.",[38,3940,3942],{"id":3941},"como-fazer-na-sua-máquina","Como fazer na sua máquina",[751,3944,3946],{"id":3945},"_1-crie-o-diretório-de-skills","1. Crie o diretório de skills",[102,3948,3950],{"className":757,"code":3949,"language":759,"meta":107,"style":107},"mkdir -p ~/.claude/skills\n",[26,3951,3952],{"__ignoreMap":107},[111,3953,3954,3957,3960],{"class":113,"line":114},[111,3955,3956],{"class":792},"mkdir",[111,3958,3959],{"class":124}," -p",[111,3961,3962],{"class":143}," ~/.claude/skills\n",[751,3964,3966],{"id":3965},"_2-crie-uma-skill","2. Crie uma skill",[11,3968,3969,3970,3973],{},"Cada skill é uma pasta com um ",[26,3971,3972],{},"skill.md"," dentro:",[102,3975,3977],{"className":757,"code":3976,"language":759,"meta":107,"style":107},"mkdir -p ~/.claude/skills/security-scan\n",[26,3978,3979],{"__ignoreMap":107},[111,3980,3981,3983,3985],{"class":113,"line":114},[111,3982,3956],{"class":792},[111,3984,3959],{"class":124},[111,3986,3987],{"class":143}," ~/.claude/skills/security-scan\n",[11,3989,3990,3991,3993],{},"O arquivo ",[26,3992,3972],{}," tem frontmatter YAML + instruções em markdown:",[102,3995,3997],{"className":2248,"code":3996,"language":2250,"meta":107,"style":107},"---\nname: security-scan\ndescription: Quando usar esta skill\n---\n\n# Security Scan\n\n## Steps\n\n### 1. Identifique os arquivos alterados\n### 2. Leia cada arquivo\n### 3. Verifique contra [categoria]\n### 4. Reporte com severidade + fix\n",[26,3998,3999,4004,4009,4014,4018,4022,4027,4031,4036,4040,4045,4050,4055],{"__ignoreMap":107},[111,4000,4001],{"class":113,"line":114},[111,4002,4003],{},"---\n",[111,4005,4006],{"class":113,"line":121},[111,4007,4008],{},"name: security-scan\n",[111,4010,4011],{"class":113,"line":131},[111,4012,4013],{},"description: Quando usar esta skill\n",[111,4015,4016],{"class":113,"line":140},[111,4017,4003],{},[111,4019,4020],{"class":113,"line":150},[111,4021,814],{"emptyLinePlaceholder":813},[111,4023,4024],{"class":113,"line":158},[111,4025,4026],{},"# Security Scan\n",[111,4028,4029],{"class":113,"line":166},[111,4030,814],{"emptyLinePlaceholder":813},[111,4032,4033],{"class":113,"line":174},[111,4034,4035],{},"## Steps\n",[111,4037,4038],{"class":113,"line":182},[111,4039,814],{"emptyLinePlaceholder":813},[111,4041,4042],{"class":113,"line":188},[111,4043,4044],{},"### 1. Identifique os arquivos alterados\n",[111,4046,4047],{"class":113,"line":194},[111,4048,4049],{},"### 2. Leia cada arquivo\n",[111,4051,4052],{"class":113,"line":202},[111,4053,4054],{},"### 3. Verifique contra [categoria]\n",[111,4056,4057],{"class":113,"line":210},[111,4058,4059],{},"### 4. Reporte com severidade + fix\n",[11,4061,4062,4063,4066],{},"O segredo está na ",[48,4064,4065],{},"especificidade",". Não escreva \"verifique se tem vulnerabilidades\". Escreva exatamente o que verificar, com exemplos de código vulnerável e seguro, com patterns de grep, com referências a critérios específicos (CC6.3, PI1.1, A01).",[751,4068,4070],{"id":4069},"_3-ensine-o-contexto-do-seu-projeto-no-claudemd","3. Ensine o contexto do seu projeto no CLAUDE.md",[11,4072,4073,4074,4076],{},"No ",[26,4075,73],{}," do seu projeto, eu coloquei regras como:",[2849,4078,4079,4085,4095,4106],{},[2310,4080,4081,4082],{},"Toda alteração de segurança atualiza ",[26,4083,4084],{},"docs/security/AUDIT_2026.md",[2310,4086,4087,4088,4091,4092,4094],{},"Todo commit passa pelo skill ",[26,4089,4090],{},"/commit"," que roda ",[26,4093,3127],{}," automaticamente",[2310,4096,4097,4098,354,4100,354,4102,354,4104],{},"Skills disponíveis: ",[26,4099,2781],{},[26,4101,3046],{},[26,4103,3127],{},[26,4105,4090],{},[2310,4107,4108],{},"Stack: Fastify 5 + Zod + Mongoose 8 + ioredis",[11,4110,4111],{},"O Claude Code absorve esse contexto e aplica as skills com conhecimento do projeto.",[751,4113,4115],{"id":4114},"_4-use-no-fluxo-natural-de-desenvolvimento","4. Use no fluxo natural de desenvolvimento",[11,4117,4118],{},"Não é um passo extra. É parte do fluxo:",[102,4120,4125],{"className":4121,"code":4123,"language":4124},[4122],"language-text","1. Implementa feature\n2. /security-scan         ← audita os arquivos alterados\n3. Corrige findings\n4. /query-review           ← verifica queries MongoDB\n5. /commit                 ← que roda /docs automaticamente\n","text",[26,4126,4123],{"__ignoreMap":107},[11,4128,4129],{},"Periodicamente:",[102,4131,4134],{"className":4132,"code":4133,"language":4124},[4122],"/security-hardening        ← auditoria profunda\n/soc2-audit                ← compliance completa\n",[26,4135,4133],{"__ignoreMap":107},[38,4137,4139],{"id":4138},"por-que-isso-importa-mais-que-um-scanner-externo","Por que isso importa mais que um scanner externo",[11,4141,4142,4143,100],{},"Um scanner externo vê o código como texto. O Claude Code vê o código como ",[48,4144,4145],{},"sistema",[11,4147,4148,4149,4152,4153,4156,4157,4160,4161,100],{},"Ele sabe que o ",[26,4150,4151],{},"clearFailedAttempts"," na linha 69 do ",[26,4154,4155],{},"auth.service.ts"," é chamado ",[48,4158,4159],{},"antes"," da verificação 2FA, e que isso permite brute-force infinito do TOTP, resetando o counter a cada tentativa. Nenhum scanner de regex encontra isso. É uma vulnerabilidade de ",[48,4162,4163],{},"lógica de negócio",[11,4165,4166,4167,4170,4171,4174],{},"Ele sabe que aquele ",[26,4168,4169],{},"Account.findById(id)"," sem segundo argumento vai retornar o documento inteiro incluindo o hash da senha, o secret do 2FA, e os backup codes. Não porque bateu uma regra \"findById sem projeção\", mas porque ",[48,4172,4173],{},"leu o schema do Mongoose"," e entendeu quais campos existem e quais são sensíveis.",[11,4176,4177,4178,4180,4181,4183],{},"Ele sabe cruzar o finding C2 do ",[26,4179,2829],{}," com o teste de regressão no ",[26,4182,3806],{}," e confirmar que o fix foi implementado e testado.",[11,4185,4186],{},"Isso não é scan. É auditoria.",[38,4188,4190],{"id":4189},"a-ferramenta-sempre-esteve-ali","A ferramenta sempre esteve ali",[11,4192,4193],{},"Skills são arquivos markdown. Qualquer dev pode criar. Não precisa esperar a Anthropic lançar uma feature oficial. Não precisa pagar uma plataforma de segurança.",[11,4195,4196,4197,4200],{},"O Claude Code já lê código, já entende contexto, já sabe escrever testes, já conhece OWASP e SOC 2. A única coisa que faltava era alguém dizer pra ele ",[48,4198,4199],{},"como"," auditar, com a especificidade de quem realmente faz segurança.",[11,4202,4203],{},"As empresas de segurança não perderam bilhões porque surgiu mais um scanner. Perderam porque o paradigma mudou: a segurança saiu de fora e foi pra dentro do código. E quando está dentro, não tem como competir com quem entende cada linha.",[2398,4205,4206],{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}",{"title":107,"searchDepth":121,"depth":121,"links":4208},[4209,4210,4211,4225,4235,4241,4242],{"id":2713,"depth":121,"text":2714},{"id":2730,"depth":121,"text":2731},{"id":2766,"depth":121,"text":2767,"children":4212},[4213,4215,4217,4219,4221,4223],{"id":2788,"depth":131,"text":4214},"/security-scan: OWASP + SOC 2",{"id":2840,"depth":131,"text":4216},"/security-hardening: Auditoria profunda de produção",{"id":2942,"depth":131,"text":4218},"/soc2-audit: SOC 2 Type I e Type II completo",{"id":3043,"depth":131,"text":4220},"/query-review: Review específico de MongoDB",{"id":3114,"depth":131,"text":4222},"/severity-review: Review com profundidade configurável",{"id":3124,"depth":131,"text":4224},"/docs: Documentação automática de segurança",{"id":3140,"depth":121,"text":3141,"children":4226},[4227,4228,4229,4230,4231,4232,4233,4234],{"id":3147,"depth":131,"text":3148},{"id":3328,"depth":131,"text":3329},{"id":3521,"depth":131,"text":3522},{"id":3740,"depth":131,"text":3741},{"id":3797,"depth":131,"text":3798},{"id":3843,"depth":131,"text":3844},{"id":3882,"depth":131,"text":3883},{"id":3892,"depth":131,"text":3893},{"id":3941,"depth":121,"text":3942,"children":4236},[4237,4238,4239,4240],{"id":3945,"depth":131,"text":3946},{"id":3965,"depth":131,"text":3966},{"id":4069,"depth":131,"text":4070},{"id":4114,"depth":131,"text":4115},{"id":4138,"depth":121,"text":4139},{"id":4189,"depth":121,"text":4190},"2026-02-23","Ferramentas de segurança existem há décadas. A diferença real não é a ferramenta. É que agora a auditoria acontece de dentro do código. E eu já fazia isso com skills.",{},"/blog/claude-code-security-ja-estava-disponivel",{"title":2702,"description":4244},"blog/claude-code-security-ja-estava-disponivel",[2427,2428,2429,4250,4251],"soc2","owasp","2GmQLwn87MxJ_SB0vuf0FEJWR67uAQtODT6VNxtkOwY",1772834005820]