Documentação
Como o Basalt lê seu código e sua cloud, o que ele encontra e o que fazer com isso.
Visão geral
O Basalt monta um grafo da sua plataforma a partir de duas fontes: seus repositórios e sua conta AWS. Depois, procura nesse grafo caminhos de ataque, ou seja, cadeias de permissões e conexões reais que levam de algo que um atacante consegue alcançar até dados ou sistemas que importam.
Alguns caminhos passam por agentes de IA. Uma função pública hospeda um agente, o agente tem uma tool que executa comandos de shell e a role IAM da função consegue ler um bucket de clientes. Outros não envolvem agente nenhum, como uma role IAM que qualquer conta AWS pode assumir. O Basalt reporta os dois tipos.
Todo achado vem com o caminho, o impacto, uma correção sugerida e a evidência que o sustenta: arquivo e linha, ou o recurso que define cada etapa.
- O código é analisado, nunca importado nem executado.
- O acesso à AWS é somente leitura e limitado a uma lista explícita de chamadas de configuração. Veja Ações na AWS.
- Valores de secrets nunca são armazenados. O grafo guarda apenas nomes, locais e tipos.
Para o modelo de confiança completo, veja Segurança.
Início rápido
A CLI basalt analisa um repositório na sua máquina. Instale com curl -fsSL <sua URL do Basalt>/api/v1/cli/install.sh | sh (macOS, Linux) ou pela página de download (também Windows). Todo comando exige uma conta vinculada; a gratuita basta.
1. Vincule seu terminal
basalt login vincula a CLI à sua conta do Basalt. O comando mostra uma URL e um código, abre o navegador e espera até você confirmar o código lá. Em uma máquina remota, use --no-browser e abra a URL você mesmo.
$ basalt login $ basalt whoami
O token fica salvo em ~/.config/basalt/credentials.json com permissão 0600. basalt logout revoga a sessão no servidor e apaga o arquivo.
Todo comando exige uma conta vinculada. Depois disso, basalt scan, basalt scan-aws e basalt check rodam inteiramente na sua máquina e não enviam resultados ao Basalt; só o assistente interativo (basalt sem argumentos) fala com a API do Basalt.
2. Analise um repositório
$ basalt scan path/to/repo
O scanner percorre o repositório, faz o parsing de arquivos Python, TypeScript e JavaScript, Java, Rust, C++, Terraform e de configuração de MCP, monta o grafo e roda o motor de caminhos. Pastas de build e de dependências, como node_modules, .venv, dist, build, .next e target, são ignoradas, assim como arquivos acima de 1 MB.
3. Leia a saída
A saída em texto tem três partes: as correções que fecham os caminhos mais importantes, os achados ordenados por severidade e um inventário do que foi encontrado.
11 findings, 12 fixes
== Fix first ==
- Scope s3:GetObject on role support-bot (closes 3)
- Remove shell execution from run_diagnostics (closes 1)
- Restrict who can assume ci-deployer (closes 1)
== Findings ==
[CRITICAL] Any AWS account can assume ci-deployer (administrator)
Any AWS account -> ci-deployer -> acme-customer-exports
[CRITICAL] SupportBot can be prompted to read acme-customer-exports through run_diagnostics
support-bot function URL -> support-bot -> SupportBot (prompt injection) -> run_diagnostics (runs commands) -> support-bot -> acme-customer-exports
[CRITICAL, medium confidence] SupportBot can be prompted to read acme-customer-exports through read_file
...
[HIGH] INTERNAL_API_TOKEN is stored in plain text
run_diagnostics -> INTERNAL_API_TOKEN
Scanned 30 files: 9 Agent, 27 Tool, 10 McpServer, 13 Credential, 3 EntryPoint, 3 ComputeResource, 4 IamRole, 4 DataStore
...
Cada linha de achado mostra a severidade e, quando não é alta, a confiança. A linha logo abaixo é o caminho, etapa por etapa. Comece por Fix first: uma única correção muitas vezes fecha vários caminhos. Use --format json para obter os achados completos, com descrição, impacto e evidência (veja Saída em JSON).
Conectar a AWS
O Basalt lê sua conta AWS por meio de uma role IAM criada por você. A role confia na conta AWS do Basalt e exige um ExternalId exclusivo da sua organização, o que protege contra o problema do confused deputy. O Basalt nunca armazena chaves permanentes.
Crie a role
- Abra Sources no seu workspace e inicie a configuração da AWS. A tela mostra o ID da conta do Basalt, o seu ExternalId e as duas policies abaixo já com os valores preenchidos.
- Crie uma role chamada
basalt-readonlycom a trust policy. - Anexe a policy de permissões a ela.
- Cole o ARN da role de volta na tela de configuração.
Policy de permissões
O primeiro statement permite apenas as leituras de configuração que o Basalt usa. O segundo é um Deny explícito para leituras de dados, para que a role continue segura mesmo que ganhe mais permissões por engano.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "BasaltReadConfiguration",
"Effect": "Allow",
"Action": [
"iam:GetAccountAuthorizationDetails",
"iam:GetPolicy",
"iam:GetPolicyVersion",
"s3:ListAllMyBuckets",
"s3:GetBucketPolicy",
"s3:GetBucketPublicAccessBlock",
"s3:GetBucketTagging",
"dynamodb:ListTables",
"secretsmanager:ListSecrets",
"lambda:ListFunctions",
"lambda:GetFunctionUrlConfig",
"ecs:ListTaskDefinitionFamilies",
"ecs:DescribeTaskDefinition",
"ec2:DescribeInstances"
],
"Resource": "*"
},
{
"Sid": "BasaltNeverReadsData",
"Effect": "Deny",
"Action": [
"s3:GetObject*",
"secretsmanager:GetSecretValue",
"dynamodb:GetItem",
"dynamodb:BatchGetItem",
"dynamodb:Query",
"dynamodb:Scan",
"ssm:GetParameter*",
"kms:Decrypt",
"rds-data:ExecuteStatement"
],
"Resource": "*"
}
]
}
Trust policy
Substitua os dois placeholders pelos valores da sua tela de configuração.
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": { "AWS": "arn:aws:iam::<BASALT_ACCOUNT_ID>:root" },
"Action": "sts:AssumeRole",
"Condition": { "StringEquals": { "sts:ExternalId": "<CUSTOMER_EXTERNAL_ID>" } }
}]
}
O que acontece em cada scan
- O Basalt chama
sts:AssumeRolecom o seu ExternalId e recebe credenciais que expiram em uma hora. - Toda chamada de API passa por uma trava do lado do cliente. Qualquer operação fora da lista permitida é bloqueada antes de a requisição sair do scanner, incluindo leituras de dados como
s3:GetObjectesecretsmanager:GetSecretValue. - Cada chamada fica registrada no log de auditoria do scan: serviço, operação, região e horário.
lambda:ListFunctionseecs:DescribeTaskDefinitiondevolvem os valores das variáveis de ambiente; a AWS não oferece uma variante sem eles. O Basalt classifica cada valor em memória (parece um secret em texto puro?) e o descarta. O valor nunca é gravado, logado nem enviado.
Se preferir, remova essas duas ações. O scan continua rodando, só que sem a análise de compute, e o relatório indica a permissão que faltou.
Modo local
basalt scan-aws roda o mesmo scan somente leitura a partir da sua máquina, com a mesma trava. Você pode assumir a role do cliente:
$ basalt scan-aws --role-arn arn:aws:iam::123456789012:role/basalt-readonly --external-id <EXTERNAL_ID>
ou usar as credenciais já configuradas no seu shell ou em um profile nomeado:
$ basalt scan-aws --use-current-credentials --profile my-profile --region us-east-1 --region eu-west-1
É obrigatório informar --role-arn ou --use-current-credentials; a CLI nunca escolhe um alvo sozinha. --role-arn exige --external-id. Sem --region, só us-east-1 é analisada. O suporte à AWS precisa do boto3; a CLI avisa se ele não estiver instalado.
Conectar o GitHub
O GitHub App para análise de repositórios está em liberação gradual e ainda não está disponível para todos. Ele vai ler apenas os repositórios que você selecionar.
Enquanto ele não chega ao seu workspace, analise os repositórios com a CLI (basalt scan), ou configuramos a conexão junto com você durante o acesso antecipado. Também dá para analisar o Terraform de um repositório sem conectar a AWS: o scanner lê os arquivos .tf diretamente.
Verificação antes do deploy
basalt check responde a uma pergunta: este branch adiciona caminhos de ataque em relação à sua base? O comando analisa a working tree, incluindo mudanças ainda não commitadas, e o merge-base com o branch base, e depois compara os achados.
$ basalt check $ basalt check --base origin/main --fail-on critical
A árvore base é extraída com git archive para um diretório temporário e apenas analisada. Nada no seu repositório é alterado. Sem --base, a verificação usa o branch padrão do remote e, depois, tenta origin/main, origin/master, main e master.
A saída agrupa os caminhos em três listas: caminhos novos introduzidos pelo branch, caminhos que o branch fecha e caminhos que já estavam abertos na base. O veredito é um destes:
- block: pelo menos um caminho novo com severidade igual ou acima de
--fail-on(padrãohigh;neverdesativa o bloqueio). - warn: caminhos novos de severidade menor, mudanças não commitadas, commits não enviados, branch sem upstream ou branch atrás da base.
- pass: nenhuma das situações acima.
Exit codes
| Código | Significado |
|---|---|
0 | Pass ou warn. |
1 | Block: um caminho novo com severidade igual ou acima de --fail-on. |
2 | Não foi possível rodar a verificação, por exemplo porque o diretório não é um repositório git ou o branch base não foi encontrado. |
No CI
Pipelines não fazem o login pelo navegador. Na página CLI do app, em Signed-in terminals, use Create CI token: dê um nome (por exemplo github-actions) e uma validade (30, 90 ou 365 dias). O token (bsl_ci_…) aparece uma vez: salve como o secret BASALT_TOKEN do repositório. Ele só libera o scanner local (basalt scan e basalt check) e é validado na API a cada execução; não lê seus achados nem usa o assistente. Revogue na mesma página. A verificação precisa do histórico do git até o merge-base, então busque o histórico completo.
name: basalt
on: pull_request
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install the Basalt CLI
run: |
curl -fsSL https://basalt.dev/api/v1/cli/install.sh | sh
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
- name: Pre-deploy check
run: basalt check --base origin/${{ github.base_ref }} --fail-on high
env:
BASALT_TOKEN: ${{ secrets.BASALT_TOKEN }}
Use --format json para consumir o resultado em outra ferramenta.
O grafo
Tudo o que o Basalt sabe sobre a sua plataforma é um grafo de nós e arestas. Os IDs dos nós são estáveis, então o mesmo componente mantém o mesmo ID entre scans, e uma definição no Terraform e o recurso real na AWS convergem para o mesmo nó quando o nome é um literal.
Nós
| Tipo | O que representa |
|---|---|
EntryPoint | Onde entra input externo: uma function URL pública do Lambda, uma rota HTTP cujo handler chama um modelo. |
Api | Uma superfície de API. Reservado no modelo; os detectores atuais não o produzem. |
Agent | Um agente LLM ou uma chamada de modelo que recebe tools. |
Tool | Uma função que o modelo pode chamar, com suas capacidades: shell, sistema de arquivos, http, banco de dados, cloud, controle de versão. |
McpServer | Um servidor MCP declarado na configuração de um cliente ou implementado em código. |
Credential | Nome, local e tipo de armazenamento de uma credencial. Nunca o valor. |
IamRole | Uma role IAM, vinda do Terraform ou da conta real. |
Policy | Uma policy IAM. Reservado no modelo; hoje as policies são avaliadas e viram arestas can_read e can_write. |
DataStore | Um bucket S3, uma tabela DynamoDB ou um secret do Secrets Manager. |
ComputeResource | Uma função Lambda, uma task definition do ECS ou uma instância EC2. |
Arestas
| Tipo | Significado |
|---|---|
exposes | Um ponto de entrada expõe um recurso de compute. |
routes_to | Uma rota HTTP envia seu input para um agente. |
invokes | Um componente chama outro. Reservado no modelo; ainda não é produzido. |
uses_tool | Um agente pode chamar uma tool. |
provides_tool | Um servidor MCP fornece uma tool. |
uses_credential | Um componente lê uma credencial. |
assumes | Um recurso de compute roda com uma role IAM. |
can_read, can_write | Uma role pode ler ou modificar um data store, conforme calculado pelo avaliador de IAM. |
runs_on | Um agente definido no código roda em um recurso de compute, por exemplo o Lambda cujo handler aponta para o arquivo dele. |
Toda aresta carrega um nível de confiança e a evidência que a gerou.
Caminhos de ataque
Uma propriedade arriscada sozinha não é um achado. Uma tool que executa comandos de shell só importa se input não confiável consegue chegar até ela. Os detectores anexam sinais de risco aos nós, e o motor de caminhos só transforma sinais em achados quando um caminho os liga a algo que um atacante consegue alcançar e a algo que vale a pena alcançar.
O motor aplica cinco regras, descritas em Tipos de caminho. Cada achado tem uma chave estável, uma descrição em linguagem simples, o caminho, o impacto, as correções que o fecham e a evidência. Os achados são ordenados por severidade e depois por confiança. As correções são ordenadas pelo achado mais grave que fecham e depois pela quantidade de achados que fecham, então uma única mudança de escopo no IAM que fecha três caminhos aparece primeiro.
O motor nunca inventa o que não consegue ver: nada de contagem de registros, nada de conteúdo de dados, nenhuma explorabilidade além dos sinais que encontrou.
Severidade
A severidade é critical, high, medium ou low. Ela combina quatro fatores:
| Fator | Pergunta | Como o motor usa hoje |
|---|---|---|
| Exposição | O ponto de partida pode ser alcançado pela internet ou por usuários não confiáveis? | Caminhos via agente só começam em pontos de entrada públicos. Trust aberto começa em qualquer conta AWS. Buckets públicos começam na internet. |
| Explorabilidade | Cada etapa pode de fato ser executada? | Uma tool precisa repassar o input do modelo para uma chamada perigosa sem restrição. Sinais de baixa confiança, como uma tool que valida o input ou pede aprovação humana, não geram caminhos. |
| Privilégio | Quão amplas são as permissões obtidas? | Acesso de administrador em uma role com trust aberto torna o achado crítico. Recursos com wildcard geram uma correção de escopo separada. |
| Sensibilidade do dado | Aonde o caminho chega? | Um data store é tratado como sensível quando o nome ou as tags sugerem isso. Alvos sensíveis elevam caminhos via agente e de trust aberto de alto para crítico. |
Um data store parece sensível quando o nome contém customer, user, pii, export, billing, payment, patient, backup, prod ou secret, ou quando a chave ou o valor de uma tag contém pii, confidential, sensitive, restricted ou customer. O achado informa em qual tag se baseou.
Confiança
Toda aresta e todo achado têm um nível de confiança: high, medium ou low. Um caminho é tão confiável quanto a sua etapa mais fraca.
- Alta: todas as etapas foram lidas diretamente do código, da configuração ou dos metadados da cloud.
- Média: parte do caminho foi inferida. Exemplos: a role dá acesso apenas a alguns prefixos do S3; o handler de um Lambda aponta para a saída do build e o Basalt associou ao código-fonte TypeScript; uma tool lê credenciais do ambiente ou de um endpoint de metadados, o que depende do runtime.
- Baixa: uma etapa-chave não pôde ser confirmada. Exemplos: uma policy tem um bloco Condition ou uma referência do Terraform que o Basalt não consegue resolver; vários arquivos de um monorepo podem corresponder ao mesmo handler.
Quando o avaliador de IAM não consegue decidir se uma role alcança um data store, ele não chuta. A aresta é mantida com "decision": "uncertain" e confiança baixa, e a saída em texto a marca como uncertain. Um caminho de baixa confiança é reportado um nível de severidade abaixo e sinalizado, então um crítico vira alto. Trate achados de baixa confiança como pistas a verificar.
Excluir arquivos
Adicione um arquivo .basaltignore na raiz do repositório para excluir código gerado, código vendorizado ou fixtures de teste. Ele aceita um subconjunto da sintaxe do .gitignore:
- Um nome simples corresponde em qualquer profundidade:
fixtures. - Um padrão que contém
/, ou que começa com/, fica ancorado na raiz:/tests. - Uma
/no final corresponde só a diretórios:fixtures/. *,?e[]funcionam como curingas. Linhas que começam com#são comentários.- Negação (
!) não é suportada; essas linhas são ignoradas.
# dados sintéticos, sem deploy
fixtures/
/tests
*.generated.json
Os caminhos excluídos aparecem na auditoria do scan (audit.ignored), e a saída em texto informa quantos foram ignorados.
Tamanho do código
Os limites dos planos contam as linhas de código analisadas: linhas não vazias nos arquivos que o scanner processa (código nas linguagens suportadas, Terraform e configuração de MCP), somadas sobre o scan mais recente de cada repositório. Arquivos que o scanner não processa, como Markdown, imagens ou caminhos excluídos, não contam. Se um scan passar do limite do seu plano, ele falha com uma explicação; excluir código gerado ou vendorizado com .basaltignore costuma resolver. Cada scan informa o número em audit.lines_of_code.
Tipos de caminho
Agente até o dado
Um ponto de entrada público expõe um recurso de compute que hospeda um agente. Um texto enviado ao ponto de entrada pode instruir o agente (prompt injection) a chamar uma das suas tools. Se a tool repassa o input do modelo para uma chamada perigosa sem restrição, ela age com a role IAM do recurso de compute, e o motor segue essa role até cada data store que ela consegue ler ou modificar. A forma como a tool obtém a role depende do que ela faz: uma tool de shell executa comandos com as credenciais da role; uma tool de cloud chama a AWS com input escolhido pelo modelo e só alcança data stores dos serviços que chama; uma tool de sistema de arquivos no Lambda consegue ler as credenciais temporárias da role no ambiente do processo; uma tool de HTTP no ECS ou no EC2 consegue buscá-las no endpoint de credenciais. Esses dois últimos casos ficam limitados a confiança média. O achado é crítico se o data store parece sensível e alto caso contrário. Data stores que já são públicos ficam de fora, porque o caminho não acrescenta nenhum acesso novo. Existem duas variantes: uma tool de shell que não alcança nenhum data store ainda é reportada como execução de comandos crítica, e uma tool de banco de dados que executa SQL escrito pelo modelo é reportada como alta. Hoje, os pontos de entrada públicos dessa regra são function URLs do Lambda com autenticação definida como none, lidas do Terraform ou da conta real.
Trust aberto
A trust policy de uma role IAM permite que qualquer principal da AWS ("AWS": "*") a assuma, então qualquer pessoa com uma conta AWS consegue obter as permissões dela. O achado é crítico se a role tem acesso de administrador ou alcança um data store sensível, e alto caso contrário. A confiança é alta porque a trust policy é lida diretamente. A correção indica exatamente quais contas ou roles precisam da role e adiciona condições, como um ExternalId.
Credencial em texto puro
O valor de uma credencial está escrito em texto puro onde deveria haver uma referência: no env ou nos headers da configuração de um servidor MCP, em uma connection string passada a um servidor MCP ou nas variáveis de ambiente de uma função Lambda ou de uma task definition do ECS (do Terraform ou da conta real). Qualquer pessoa que consiga ler esse arquivo ou essa configuração, por acesso ao repositório, logs de CI ou backups, consegue usá-la. O achado é alto, com confiança alta. O valor em si é classificado e descartado; o Basalt guarda apenas o nome e o local. A correção é rotacionar a credencial e carregá-la de um cofre de secrets.
Servidores MCP de assistentes de código
Os servidores MCP da configuração de um repositório são carregados por todo desenvolvedor que abre o repositório no Claude Code, Cursor, VS Code ou Windsurf. Conteúdo que o assistente lê, como issues, arquivos ou páginas web, pode instruí-lo a usar esses servidores. O achado é médio quando um servidor dá acesso a shell, acesso ao sistema de arquivos em um caminho amplo (a raiz, um diretório home ou um drive) ou conversa com um servidor remoto por HTTP sem criptografia. É baixo quando o único problema é um pacote executado sem versão fixada, de modo que qualquer release nova roda na próxima inicialização. O impacto é na máquina do desenvolvedor, não em produção.
Data store sensível público
A policy ou a ACL de um bucket S3 permite leitura anônima, e o nome ou as tags dele sugerem dados sensíveis. O achado é alto, com confiança alta. Buckets públicos que não parecem sensíveis, como assets de um site estático, não são reportados. A correção remove o statement público e ativa o Block Public Access.
O que detectamos
Todo o código passa por parsing: Python com a AST dele, as outras linguagens com gramáticas tree-sitter. Nada é importado, compilado nem executado. Arquivos em linguagens que não são Python e que não mencionam nenhum framework de IA são ignorados antes do parsing.
| Fonte | Arquivos | O que o Basalt encontra |
|---|---|---|
| Python | .py | LangChain e LangGraph, LlamaIndex, CrewAI, AutoGen, OpenAI SDK e Agents SDK, Anthropic SDK, PydanticAI, smolagents, MCP SDK. Tools com decorator e schemas de tool em JSON puro, o que cada tool consegue fazer e se um parâmetro da tool chega a essa chamada, agentes e suas tools, chamadas diretas a LLM com tools=, rotas FastAPI e Flask cujo handler chama o modelo (e se exigem autenticação), variáveis de ambiente com cara de secret e campos de settings do pydantic (só os nomes). |
| TypeScript, JavaScript | .ts .tsx .mts .cts .js .jsx .mjs .cjs | Vercel AI SDK, OpenAI Agents SDK, LangChain.js e LangGraph.js, Mastra, SDKs da Anthropic e da OpenAI com loops de tools escritos à mão, servidores MCP feitos com o MCP SDK para TypeScript. |
| Java | .java | Métodos @Tool do LangChain4j e do Spring AI, @McpTool do Spring AI MCP, agentes construídos com AiServices, ChatClient ou Google ADK. |
| Rust | .rs | Servidores rmcp com #[tool], tools e agentes do rig, funções #[tool] e #[rig_tool]. |
| C++ | .cpp .cc .cxx .hpp .hh .hxx | Servidores MCP escritos com APIs no estilo do cpp-mcp. Loops de LLM escritos à mão não são detectados como agentes. |
| Configuração de cliente MCP | mcp.json, .mcp.json, claude_desktop_config.json, mcp_config.json, inclusive dentro de .cursor/, .vscode/ e .windsurf/ | Servidores do Claude Desktop, Claude Code, Cursor, VS Code e Windsurf: transporte, pacote e versão fixada, capacidades conhecidas de servidores populares, caminhos de sistema de arquivos permitidos, credenciais passadas em env ou headers. Servidores desconhecidos não recebem capacidades presumidas. |
| Terraform | .tf | Roles e policies IAM (inline, gerenciadas, attachments, policy documents, jsonencode, file(), JSON em heredoc), permission boundaries, trust policies, Lambda, task definitions do ECS, instâncias EC2, S3, DynamoDB, Secrets Manager, function URLs públicas do Lambda, bucket policies e ACLs públicas. Valores montados a partir de variables, locals ou modules são reportados como não resolvidos, nunca presumidos. |
| Conta AWS | Ao vivo, somente leitura | Roles, policies inline e gerenciadas, relações de trust, buckets S3 com policies públicas e tags, tabelas DynamoDB, secrets do Secrets Manager (nomes e tags), funções Lambda e function URLs, task roles do ECS, instance profiles do EC2. Veja Ações na AWS. |
Código e cloud são ligados pelos handlers do Lambda: um agente definido no arquivo para o qual o handler de uma função aponta roda nessa função, nos runtimes Python, Node.js e Java.
Ações na AWS
Esta é a lista completa de chamadas à AWS que o scanner pode fazer. A trava aplica essa lista no código, e um teste verifica que o scanner não chama mais nada.
| Ação IAM | Para que serve |
|---|---|
iam:GetAccountAuthorizationDetails | Roles, policies inline e gerenciadas, relações de trust (uma chamada paginada) |
iam:GetPolicy, iam:GetPolicyVersion | Conteúdo das policies gerenciadas pela AWS que estão anexadas |
s3:ListAllMyBuckets | Lista de buckets |
s3:GetBucketPolicy, s3:GetBucketPublicAccessBlock | Detectar buckets públicos |
s3:GetBucketTagging | Sensibilidade a partir das tags |
dynamodb:ListTables | Lista de tabelas, nunca os itens |
secretsmanager:ListSecrets | Nomes e tags dos secrets, nunca os valores |
lambda:ListFunctions, lambda:GetFunctionUrlConfig | Funções, suas roles, URLs públicas |
ecs:ListTaskDefinitionFamilies, ecs:DescribeTaskDefinition | Task roles, nomes de variáveis de ambiente e de secrets |
ec2:DescribeInstances | Instance profiles (roles) das instâncias |
sts:AssumeRole | Credenciais temporárias para a sua role, com o seu ExternalId |
sts:GetCallerIdentity | Confirmar a conta (não exige permissão) |
Comandos da CLI
| Comando | Opções | Descrição |
|---|---|---|
basalt, basalt chat [prompt] | Sessão interativa com o assistente do Basalt. Exige uma conta vinculada e um terminal. Toda mudança que ele propõe (edição de arquivo, branch, commit, push, pull request) é mostrada e precisa do seu sim. Ele nunca faz commit no branch padrão. | |
basalt login | --no-browser | Vincula este terminal à sua conta com um código confirmado no navegador. |
basalt logout | Revoga a sessão deste terminal e apaga o token local. | |
basalt whoami | Mostra a organização, o plano, o usuário e o papel vinculados (ou o token de CI em BASALT_TOKEN). Sai com 1 se não houver sessão. | |
basalt update | --check, -y, --yes | Instala a versão mais nova da CLI a partir do Basalt de onde ela foi baixada (confere o sha256). Sem uv, mostra o comando do instalador. |
basalt mcp | Servidor MCP por stdio com ferramentas somente leitura para Claude Code, Cursor e VS Code. Veja MCP. | |
basalt --version | Mostra a versão instalada. | |
basalt scan <path> | --format text|json, -o, --output FILE | Analisa um repositório local, somente leitura. |
basalt check [path] | --base REF, --fail-on critical|high|medium|low|never (padrão high), --format text|json | Caminhos de ataque que este branch adiciona ou fecha em relação à base. O caminho padrão é o diretório atual. Exit codes 0, 1, 2. |
basalt scan-aws | --role-arn ARN --external-id ID ou --use-current-credentials; --profile NAME, --region REGION (pode repetir, padrão us-east-1), --format text|json, -o, --output FILE | Analisa uma conta AWS real, somente leitura. Sempre exige um alvo. |
Rode basalt --help ou basalt <command> --help para ver a mesma lista no terminal.
Em CI, defina BASALT_TOKEN (token de CI) no lugar do basalt login; veja No CI.
MCP (Claude Code, Cursor, VS Code)
basalt mcp expõe o Basalt para o seu agente de código por stdio, com ferramentas somente leitura: list_findings (caminhos de ataque), get_finding (caminho, impacto, evidência e correção), list_fixes, basalt_overview e scan_path (scan local de um diretório, só parsing). Ele usa a sessão do basalt login deste computador. As ferramentas da conta fazem parte dos planos com a integração MCP (Starter em diante); scan_path funciona em qualquer conta. Nada nele edita arquivos, git ou a conta.
claude mcp add basalt -- basalt mcp
{
"mcpServers": {
"basalt": { "command": "basalt", "args": ["mcp"] }
}
}
{
"servers": {
"basalt": { "type": "stdio", "command": "basalt", "args": ["mcp"] }
}
}
Se o editor não encontrar basalt, use o caminho completo (which basalt, normalmente ~/.local/bin/basalt).
Saída em JSON
basalt scan --format json e basalt scan-aws --format json devolvem um único objeto. Resumido:
{
"scanner_version": "0.0.1",
"source": { "type": "local_repository", "root": "/path/to/repo" },
"audit": {
"files_read": [".cursor/mcp.json", "support_bot/agent.py", "infra/iam.tf"],
"lines_of_code": 611,
"ignored": ["fixtures/"],
"started_at": 1760140800.1,
"finished_at": 1760140800.9
},
"errors": [],
"graph": {
"nodes": [
{
"id": "Tool:support_bot/agent.py:run_diagnostics",
"kind": "Tool",
"name": "run_diagnostics",
"properties": { "capabilities": ["shell"], "sinks": [{ "call": "subprocess.run", "line": 32 }] },
"evidence": [{ "source": "support_bot/agent.py", "line": 29, "detail": "tool 'run_diagnostics' (decorator)" }],
"signals": [{ "id": "tool.shell_true", "summary": "subprocess called with shell=True", "confidence": "high",
"evidence": [{ "source": "support_bot/agent.py", "line": 32, "detail": "subprocess.run" }] }]
}
],
"edges": [
{
"source": "IamRole:support-bot",
"target": "DataStore:s3:acme-customer-exports",
"kind": "can_read",
"confidence": "high",
"evidence": [{ "source": "infra/iam.tf", "line": 13, "detail": "aws_iam_role_policy.support_bot_s3" }],
"properties": { "actions": ["s3:GetObject"], "decision": "allow", "scope": "full",
"prefixes": [], "via_wildcard_resource": true }
}
]
},
"analysis": {
"findings": [
{
"key": "agent-data:EntryPoint:lambda_url:support-bot|Tool:...|DataStore:s3:acme-customer-exports",
"severity": "critical",
"confidence": "high",
"title": "SupportBot can be prompted to read acme-customer-exports through run_diagnostics",
"story": "Anyone on the internet can call support-bot function URL, ...",
"path": [
{ "node_id": "EntryPoint:lambda_url:support-bot", "kind": "EntryPoint",
"name": "support-bot function URL", "relation": "exposes", "note": null }
],
"impact": "Read access to s3://acme-customer-exports (tagged data_classification=confidential).",
"fix_ids": ["fix:iam-scope:IamRole:support-bot"],
"evidence": [{ "source": "infra/compute.tf", "line": 16, "detail": "aws_lambda_function_url.support_bot" }]
}
],
"fixes": [
{
"id": "fix:iam-scope:IamRole:support-bot",
"title": "Scope s3:GetObject on role support-bot",
"summary": "The policy grants s3:GetObject on \"*\", so support-bot can reach every s3 resource ...",
"kind": "iam_policy",
"evidence": [{ "source": "infra/iam.tf", "line": 13, "detail": "aws_iam_role_policy.support_bot_s3" }],
"finding_keys": ["agent-data:..."]
}
]
}
}
source.typeélocal_repositorynoscaneaws_accountnoscan-aws, que também informaaccount_ideregions. Nesse caso, a auditoria lista cada chamada de API (service,operation,region,at) em vez de arquivos.- O
sourceda evidência é um caminho relativo ao repositório ou um ARN. - Os valores de
keydos achados são estáveis entre scans. Obasalt checkcompara essas chaves para decidir o que é novo. - O
kindde uma correção é um destes:iam_policy,tool_code,credential,mcp_config,trust_policy,data_exposure.
Limitações conhecidas
Publicamos o que a análise ainda não cobre, para você saber o que um resultado limpo significa e o que ele não significa.
- SCPs da organização, session policies e resource policies no alvo (exceto bucket policies públicas) ainda não entram na avaliação de IAM. Quando elas poderiam mudar o resultado, a aresta é marcada como incerta.
- O S3 Block Public Access é verificado só no nível do bucket, não no nível da conta.
- As function URLs do Lambda são verificadas só em
$LATEST. - Os pontos de entrada públicos vêm das function URLs do Lambda. API Gateway, load balancers e outras portas de entrada ainda não são lidos.
- Os data stores cobertos são S3, DynamoDB e Secrets Manager. RDS e EKS não são cobertos.
- Infraestrutura como código é só Terraform. CloudFormation e CDK ainda não são processados.
- Valores do Terraform montados a partir de variables, locals ou modules ficam sem resolução.
- A sensibilidade do dado vem de nomes e tags. O Basalt nunca lê dados, então não consegue ver o que um data store realmente contém nem quantos registros ele tem.
- A AWS é a única cloud. GCP e Azure não são suportados.
Dúvidas sobre como o Basalt trata seus dados e acessos? Leia Segurança. Pronto para analisar sua plataforma? Comece agora.