Pular para conteúdo

AUR-409 — Perfil de SCM falso offline (fake-scm-offline-v1)

Documento histórico do board. Pode descrever componentes removidos; para uso atual, consulte o guia do produto.

Este documento descreve a entrada fake-scm-offline-v1 publicada no registry canônico de profiles. O card publica somente documentos: nenhum engine é invocado, nenhum repositório é criado ou clonado, nenhum evento é reproduzido, nenhum pull request é simulado, e o runner permanece fail-closed para esta chave até um card de implementação dependente habilitá-la explicitamente.

Artefatos

Artefato Caminho
Schema .board/schemas/fake-scm-offline-profile.schema.json
Profile .board/oci/profiles/fake-scm-offline-v1.json
Lock .board/locks/oci/fake-scm-offline-v1.lock.json
Registry .board/oci/profiles/registry.v1.json

O registry passa a publicar exatamente nove chaves, em ordem canônica crescente: bootstrap-readonly-v1, docs-tool-offline-v1, fake-provider-v1, fake-scm-offline-v1, go-git-offline-v1, go-unit-offline-v1, parser-worker-v1, registry-v1, sqlite-offline-v1. Nenhuma chave existente muda de digest, schema ou lock.

Em ordem lexicográfica C, fake-scm- ordena depois de fake-provider- — p (0x70) precede s (0x73) no primeiro byte que difere — e antes de go-git-. A nova chave ocupa portanto o índice 3, e as cinco chaves seguintes (go-git-offline-v1, go-unit-offline-v1, parser-worker-v1, registry-v1, sqlite-offline-v1) deslocam-se uma posição à direita. Nenhuma chave sai da lista, nenhuma aridade exata vira mínimo e nenhuma igualdade vira desigualdade: cada chave continua asserida no único índice que a ordem canônica lhe dá.

Imagem pinada

O lock fixa Go e Bash na imagem de bootstrap já presente localmente, por digest imutável:

aurum-bootstrap-go-bash@sha256:503ac356fa6bca4bad56fade87b5479ff371bd446ffb9c7db91f211323c7c73e

A imagem foi escolhida por prova, não por conveniência. Sob --pull=never --network=none --read-only --user=65534:65534 --cap-drop=ALL (mais --security-opt=no-new-privileges) os dois probes locais respondem:

Imagem Bash Go Binário de SCM Transporte
aurum-bootstrap-go-bash@sha256:503ac356… 5.2.26 go1.21.13 git, hg, svn, git-lfs, gh ausentes git-upload-pack, git-receive-pack, ssh, curl ausentes; uma única interface, lo
golang@sha256:4746d264… (imagem de go-git-offline-v1) presente go1.21.13 git 2.39.2 presente git-upload-pack, git-receive-pack, ssh e curl presentes

Isso é o que determina o plano, e não o contrário. Para um SCM falso, o esperado é justamente não precisar de um SCM real: scm_backend: "fake-local", real_scm_binary: "denied", fake_scm_cgo: false e CGO_ENABLED: "0" fixam uma implementação puro Go, e é exatamente por isso que a imagem correta é a de bootstrap, que não carrega git, nem git-upload-pack, nem git-receive-pack, nem ssh, nem curl. Usar a imagem golang de go-git-offline-v1 seria trocar um plano cujas negações são estruturais por um plano cujas negações dependem de ninguém invocar as ferramentas presentes. go-git-offline-v1 continua sendo a chave dona daquele runtime; este profile é outra chave, com outro lock, e não pode tomá-lo emprestado. Um backend git, um real_scm_binary diferente de denied ou um fake ligado por cgo são rejeitados com unsafe-plan. Nenhuma imagem foi construída, baixada ou fabricada para este card.

Plano declarado

Contenção

Chave Valor
network none
user 65534:65534
cap_drop / cap_add ALL / none
mounts / devices / sockets none / none / none
pull never
tmpfs rw,nosuid,nodev
checkout_readonly true
read_only_rootfs true
no_new_privileges true
privileged false
timeout_seconds 60
memory_mb 256
cpu_millis 1000
pids_limit 128
tmpfs_mb 128
stdout_limit_bytes / stderr_limit_bytes 65536 / 65536
max_input_files / max_input_bytes 10000 / 67108864
module_cache /go/pkg/mod (module_cache_read_only: true)

Superfície de SCM

Chave Valor Efeito
scm_backend fake-local o SCM é o fake local determinístico, nunca um cliente real
real_scm_binary denied nenhum git/hg/svn do host ou da imagem é invocado
fake_scm digest-pinned o fake só é admitido content-addressed
fake_scm_cgo false fake puro Go: nenhuma lib nativa de version control
fake_scm_root /tmp/aurum-fake-scm-engine o fake vive no tmpfs limitado
event_set digest-pinned os eventos reproduzidos são pinados por conteúdo
event_root /tmp/aurum-fake-scm-events os eventos vivem no tmpfs limitado
response_set digest-pinned as respostas são pinadas por conteúdo
repository_mode ephemeral os repositórios simulados existem só durante a execução
repository_root /tmp/aurum-fake-scm-repos os repositórios vivem no tmpfs limitado, nunca no checkout
remote_origin absent não existe origem: nem https://, nem git@host:path, nem ssh://, nem file://, nem ext::
remote_protocols ["fake"] allowlist fechada: só o transporte in-process do fake
custom_transport denied nenhum upload-pack/receive-pack customizado
credential_helper denied nenhum credential helper é consultado
askpass denied nenhum programa de askpass é executado
hooks denied nenhum hook do repositório é executado
submodules denied nenhum submódulo, portanto nenhum ponteiro para fora da árvore
url_rewriting denied nenhuma reescrita insteadOf depois da validação
publication denied nenhum push, nenhuma criação de PR, nenhuma release
external_destination denied nenhum destino fora do fake local
token absent nenhum token real integra o plano
credential_sources denied nenhuma origem de credencial é consultada
host_checkout denied o checkout do host nunca é o repositório de trabalho
host_filesystem denied nenhum caminho do host é lido ou escrito
subprocess denied nenhum processo externo é criado — é assim que um git real seria alcançado
max_events 1024 tamanho exato do log de eventos admissível
max_event_bytes 65536 teto exato por evento (64 KiB)
max_responses 64 número exato de respostas pinadas
max_response_bytes 65536 teto exato por resposta (64 KiB)
max_repository_bytes 33554432 teto exato do repositório (32 MiB) dentro do tmpfs de 128 MiB
scm_timeout_seconds 5 wall-clock por operação de SCM

Cada limite é uma constante exata, nunca um mínimo: max_repository_bytes de 0 e de 34359738368 negam igualmente — o primeiro por ser ilimitado, o segundo porque o repositório simulado não caberia no tmpfs que precisa contê-lo. Independentemente das constantes, o loader exige max_repository_bytes <= tmpfs_mb * 1024 * 1024, max_events * max_event_bytes <= max_input_bytes e max_responses * max_response_bytes <= max_input_bytes; com os valores publicados, 1024 * 65536 é exatamente max_input_bytes.

fake_scm_root, event_root e repository_root são casados por expressões ancoradas nas duas pontas, então /workspace, um traversal que começa dentro do root limitado e sobe para fora dele (/tmp/aurum-fake-scm-repos/../../etc), uma barra final (/tmp/aurum-fake-scm-repos/) e um irmão que apenas compartilha o prefixo (/tmp/aurum-fake-scm-repos-host) são todos negados. Loader e camada de integração exigem ainda que os três roots sejam distintos e que nenhum contenha outro, porque um fake que morasse dentro da árvore de repositórios tornaria uma entrada pinada gravável em tempo de replay.

remote_origin também é casado por expressão ancorada (^absent$), de modo que um valor que apenas comece com o texto admitido — por exemplo absent-but-really-https://… — é negado como qualquer URL explícita.

Comandos permitidos

["go", "test", "-mod=readonly", "./..."]

Qualquer outro comando, ou o mesmo comando com aridade diferente, invalida o plano. O ambiente é fechado sobre exatamente estas chaves:

Variável Valor
GOPROXY off
GOSUMDB off
GONOSUMDB *
GOTOOLCHAIN local
CGO_ENABLED 0
AURUM_FAKE_SCM_ROOT /tmp/aurum-fake-scm-engine
AURUM_FAKE_SCM_EVENT_ROOT /tmp/aurum-fake-scm-events
AURUM_FAKE_SCM_REPOSITORY_ROOT /tmp/aurum-fake-scm-repos
GIT_CONFIG_NOSYSTEM 1
GIT_CONFIG_GLOBAL /dev/null
GIT_CONFIG_SYSTEM /dev/null
GIT_TERMINAL_PROMPT 0
GIT_ASKPASS /bin/false
GIT_ALLOW_PROTOCOL none
HTTP_PROXY (vazio)
HTTPS_PROXY (vazio)
NO_PROXY *

As seis variáveis GIT_* são defesa em profundidade, não a defesa principal: a imagem pinada não tem cliente de version control algum. Elas existem para que, mesmo se um consumidor futuro colocasse um binário real ao alcance, todos os canais de configuração de onde um credential helper, um programa de askpass, uma reescrita insteadOf ou um transporte ext:: seriam lidos já estivessem fechados no próprio plano. Cada uma é pinada por constante: GIT_ASKPASS para um askpass real, GIT_ALLOW_PROTOCOL para ext, GIT_CONFIG_NOSYSTEM para 0, GIT_CONFIG_GLOBAL para um arquivo de configuração e GIT_TERMINAL_PROMPT para 1 são mutantes provados, todos unsafe-plan.

As três variáveis AURUM_FAKE_SCM_* e os três roots declarados são duas grafias dos mesmos diretórios; o teste de integração recusa qualquer divergência entre elas, porque AURUM_FAKE_SCM_REPOSITORY_ROOT é exatamente onde um clone simulado apareceria se apontasse para fora do tmpfs limitado. Nenhuma credencial, valor de ambiente do host ou segredo entra no documento, e o loader recusa qualquer valor com forma de credencial mesmo em campo cujo valor seja pinado por constante.

Códigos de rejeição

O loader (ValidateRegistryAUR409 / ValidateProfileAUR409) é side-effect-free, não abre socket, não executa comando de version control, não cria subprocesso e devolve exatamente um código:

Código Causa
registry-missing registry ausente ou não regular
schema-invalid registry, schema ou profile fora da forma canônica (inclui chave duplicada dentro de um objeto JSON e limite obrigatório ausente, como scm_timeout_seconds)
schema-missing documento de schema ausente
schema-digest-mismatch schema_digest não reproduz os bytes do schema
profile-missing documento do profile ausente
profile-unregistered chave desconhecida, ou fake-scm-offline-v1 ausente do registry
duplicate-profile a mesma chave publicada duas vezes
order-invalid chaves fora da ordem canônica crescente
credential-present valor de ambiente com forma de credential real
digest-invalid digest vizinho fora do formato sha256:<64 hex>, ou fake, event set ou response set sem digest
unsafe-plan limite obrigatório relaxado ou excedido, tmpfs ilimitado, log de eventos ou conjunto de respostas maior que o limite de entrada, repositório fora do tmpfs, roots sobrepostos, backend real, binário de SCM real, origem remota, allowlist de transporte ampliada, transporte customizado, credential helper, askpass, hook, submódulo, reescrita de URL, publicação, destino externo, token presente, origem de credencial, checkout do host, filesystem do host, subprocesso, repositório persistente, fake cgo, canal GIT_* reaberto, rede, mount, socket, device, root ou capability
lock-missing lock ausente
mutable-input imagem por tag mutável, repositório inesperado ou lock fora do schema
lock-digest-mismatch lock_digest não reproduz os bytes do lock

As constantes de segurança do schema são pinadas duas vezes: nos bytes do documento de schema e em profileSchemaConstants409 / environmentSchemaConstants409, dentro de tests/unit/AUR-409.go. Relaxar o schema e recomputar o schema_digest no registry move os dois arquivos de forma consistente e ainda assim falha, porque o mapa em Go não se move com eles.

Mutantes provados

O acceptance executa cada mutante contra bytes reais em disco, na cópia descartável, e exige o código declarado; o restore volta byte-idêntico.

Mutante Mudança Código
MUT-001 chave fake-scm-offline-v1 duplicada com bytes diferentes duplicate-profile
MUT-002 imagem do lock por tag mutável mutable-input
MUT-003 network de none para host unsafe-plan
mount habilitado mounts para bind unsafe-plan
socket habilitado sockets para unix unsafe-plan
device habilitado devices para /dev/fuse unsafe-plan
root user para 0:0 unsafe-plan
capability adicionada cap_add para SYS_ADMIN unsafe-plan
backend real scm_backend para git unsafe-plan
binário real real_scm_binary para git unsafe-plan
origem HTTPS remote_origin para https://forge.example.com/… unsafe-plan
origem scp remote_origin para git@forge.example.com:… unsafe-plan
origem SSH remote_origin para ssh://forge.example.com/… unsafe-plan
origem file remote_origin para file:///etc unsafe-plan
origem ext remote_origin para ext::sh -c cat unsafe-plan
origem com prefixo remote_origin para absent-but-really-https://… unsafe-plan
allowlist ampliada remote_protocols ganha "https" unsafe-plan
allowlist trocada remote_protocols para ["ext"] unsafe-plan
transporte customizado custom_transport para upload-pack unsafe-plan
credential helper credential_helper para store unsafe-plan
askpass askpass para allowed unsafe-plan
hooks hooks para allowed unsafe-plan
submódulo submodules para recursive unsafe-plan
reescrita de URL url_rewriting para insteadOf unsafe-plan
publicação publication para push unsafe-plan
destino externo external_destination para allowed unsafe-plan
token presente token para present unsafe-plan
origem de credencial credential_sources para environment unsafe-plan
checkout do host host_checkout para read-write unsafe-plan
filesystem do host host_filesystem para read-only unsafe-plan
subprocesso subprocess para allowed unsafe-plan
fake cgo fake_scm_cgo para true unsafe-plan
repositório persistente repository_mode para persistent unsafe-plan
repositório no host repository_root para /workspace unsafe-plan
traversal do repositório repository_root para /tmp/aurum-fake-scm-repos/../../etc unsafe-plan
barra final repository_root para /tmp/aurum-fake-scm-repos/ unsafe-plan
irmão com mesmo prefixo repository_root para /tmp/aurum-fake-scm-repos-host unsafe-plan
eventos fora do tmpfs event_root para /workspace/.board unsafe-plan
fake fora do tmpfs fake_scm_root para /usr/local/bin unsafe-plan
repositório ilimitado max_repository_bytes para 0 unsafe-plan
repositório gigante max_repository_bytes para 34359738368 unsafe-plan
log de eventos gigante max_events para 2048 unsafe-plan
resposta ilimitada max_response_bytes para 0 unsafe-plan
tmpfs ilimitado tmpfs_mb para 0 unsafe-plan
askpass por ambiente GIT_ASKPASS para um askpass real unsafe-plan
protocolo por ambiente GIT_ALLOW_PROTOCOL para ext unsafe-plan
config de sistema GIT_CONFIG_NOSYSTEM para 0 unsafe-plan
config global GIT_CONFIG_GLOBAL para um arquivo de configuração unsafe-plan
prompt de terminal GIT_TERMINAL_PROMPT para 1 unsafe-plan
fake sem digest fake_scm para unpinned digest-invalid
eventos sem digest event_set para unpinned digest-invalid
respostas mutáveis response_set para latest digest-invalid
limite ausente scm_timeout_seconds removido do documento schema-invalid
credential real HTTP_PROXY para um token com forma real (montado em runtime) credential-present

O vetor de credential nunca aparece como literal em arquivo versionado: ele é montado em runtime, dentro da cópia descartável do acceptance, para que o portão de credenciais de oci-run não veja forma de segredo em nenhuma entrada materializada nem em nenhuma saída.

Loaders vizinhos

Registrar uma nona chave fecha a allowlist dos loaders já entregues, então este card os estende no mesmo commit:

  • tests/unit/AUR-402.go — allowlist de chaves do registry canônico.
  • tests/unit/AUR-403.go — allowlist, aridade (9) e ordem posicional.
  • tests/unit/AUR-404.go — conjunto de chaves registradas e switch do loader.
  • tests/unit/AUR-405.go — conjunto de chaves registradas e switch do loader.
  • tests/unit/AUR-406.go — conjunto de chaves registradas e switch do loader.
  • tests/unit/AUR-407.go — conjunto de chaves registradas e switch do loader.
  • tests/unit/AUR-408.go — conjunto de chaves registradas e switch do loader.
  • tests/contracts/AUR-404.go — aridade exata do registry (8 para 9).

Cada extensão é aditiva: nenhuma rejeição anterior desaparece, nenhuma igualdade vira desigualdade, nenhuma aridade exata vira mínimo e nenhuma chave sai de allowlist. Nenhum desses loaders lê os documentos do fake SCM: cada um checa apenas os caminhos declarados e a forma do digest, de modo que registrar o SCM falso não pode re-apontar silenciosamente o plano Go unit, o plano Git, o plano do fake provider, o plano do parser worker, o plano do state store nem o plano da ferramenta de documentação.

A aridade total do registry é asserida em tests/unit/AUR-409.go, que este card possui. tests/contracts/AUR-409.go não assere aridade: este card é dono da chave fake-scm-offline-v1, não do tamanho do registry, e a próxima carta de profile deve quebrar apenas onde sua posse estiver declarada.

Diagnóstico offline

# a chave está publicada e em ordem?
grep -n '"key"' .board/oci/profiles/registry.v1.json

# os digests do registry reproduzem os bytes?
sha256sum .board/schemas/fake-scm-offline-profile.schema.json \
          .board/locks/oci/fake-scm-offline-v1.lock.json
printf '%s' "$(sed -n 's/.*"image": "\(.*\)".*/\1/p' \
  .board/locks/oci/fake-scm-offline-v1.lock.json)" | sha256sum

# o runtime da imagem pinada ainda satisfaz o plano puro Go sem SCM real?
docker run --rm --pull=never --network=none --read-only \
  --user=65534:65534 --cap-drop=ALL --entrypoint= \
  aurum-bootstrap-go-bash@sha256:503ac356fa6bca4bad56fade87b5479ff371bd446ffb9c7db91f211323c7c73e \
  bash -c 'command -v go; command -v git || echo git-absent;
           command -v git-upload-pack || echo upload-pack-absent;
           command -v ssh || echo ssh-absent; ls /sys/class/net'