Sentinel es una Build Trust Platform: analiza no solo el código, sino el ecosistema completo que lo produjo. Workspace, scripts, toolchain, PATH, CI, artifacts, red, inputs, grafo de procesos, historial, tendencias, identidad del builder, contratos y confianza.
Versión: 4.0.0
Stack: TypeScript + Node.js ≥20 + Vitest
Estructura: src/cli/ (frontend CLI) + src/core/ (lógica principal) + src/core/network/ (build trust modules)
- Arquitectura General
- Build Flight Recorder
- Chain of Build Trust (10 módulos)
- 3.1 File Read Provenance
- 3.2 Build Input Identity
- 3.3 PATH Resolution
- 3.4 Trust Engine
- 3.5 Process Lifetime
- 3.6 Build Intent
- 3.7 Build Contract
- 3.8 Secret Flow
- 3.9 Hermetic Build Score
- 3.10 Compiler Invocation Identity
- Build Identity
- Build DNA & Multi-DNA
- Build DNA & Multi-DNA
- Provenance Graph
- Build Baseline & Normality
- Trend Engine
- Build Explain
- Evidence Chain
- Risk Engine
- Anti-Evasion Engine
- Behavior Engine
- Canary System
- Native Event System
- Network Audit Pipeline
- CLI Commands
- Tipos (build-types.ts)
- Testing
- Configuración
- External Validation
- Evidence Reliability Engine
- Evasion Detection
- Build Serializer
- Roadmap
┌─────────────────────────────────────────────────────────────┐
│ CLI (commander) │
│ src/cli/main.ts — scan, build, explain, history, policy... │
└──────────────────────┬──────────────────────────────────────┘
│
┌──────────────────────▼──────────────────────────────────────┐
│ Build Recorder │
│ src/cli/build/build-recorder.ts │
│ recordBuild() → BuildRecord completo │
│ Polling: processes, files, network, reads, contracts │
└──────────────────────┬──────────────────────────────────────┘
│
┌──────────────────────▼──────────────────────────────────────┐
│ Core Network Modules │
│ src/core/network/ │
│ │
│ ┌─────────────────┐ ┌──────────────┐ ┌───────────────┐ │
│ │ File Read │ │ Build Input │ │ PATH │ │
│ │ Provenance │ │ Identity │ │ Resolution │ │
│ └────────┬────────┘ └──────┬───────┘ └──────┬────────┘ │
│ │ │ │ │
│ ┌────────▼──────────────────▼──────────────────▼────────┐ │
│ │ Trust Engine │ │
│ │ 7 dimensiones ponderadas → Score 0-100 + breakdown │ │
│ └────────────────────────┬──────────────────────────────┘ │
│ │ │
│ ┌────────────────────────▼──────────────────────────────┐ │
│ │ Process │ Build │ Build │ Build │ │
│ │ Lifetime │ Intent │ Contract │ Identity │ │
│ └─────────────┴────────────┴────────────┴───────────────┘ │
│ │
│ ┌─────────────┐ ┌──────────────┐ ┌──────────────────┐ │
│ │ Build DNA │ │ Provenance │ │ Trend Engine │ │
│ │ + Multi-DNA │ │ Graph │ │ (EWMA/CUSUM) │ │
│ └─────────────┘ └──────────────┘ └──────────────────┘ │
│ │
│ ┌─────────────┐ ┌──────────────┐ ┌──────────────────┐ │
│ │ Risk Engine │ │ Anti-Evasion │ │ Canary System │ │
│ │ (behavior) │ │ Engine │ │ (decoy files) │ │
│ └─────────────┘ └──────────────┘ └──────────────────┘ │
│ │
│ ┌─────────────┐ ┌──────────────┐ │
│ │ Evidence │ │ Behavior │ │
│ │ Chain │ │ Engine │ │
│ └─────────────┘ └──────────────┘ │
└─────────────────────────────────────────────────────────────┘
Archivo: src/cli/build/build-recorder.ts (527 líneas)
Pre-build
├─ capturePathState() → PATH + 22 tools resueltas
├─ capturePreBuildInventory() → mtime/size de todos los archivos
├─ buildInputIdentity() → scan + fingerprint SHA256 de inputs
├─ baselinePids → snapshot de PIDs pre-existentes
└─ fileBaseline → snapshot de archivos
Durante (polling cada N ms)
├─ pollProcesses (200ms)
│ ├─ trackProcessExits() → detecta PIDs desaparecidos
│ ├─ captureScriptIdentity()→ scripts con imports
│ └─ hash chain link
├─ pollFiles (1000ms)
│ ├─ detect created/modified/deleted
│ └─ hash chain link
├─ pollNetwork (2000ms)
│ ├─ TCP connections
│ └─ DNS queries
├─ pollReads (1500ms)
│ ├─ pollProcessOpenFiles() → archivos abiertos por PID
│ └─ dedup
Post-build
├─ detectReadFilesPostBuild() → inferencia por mtime
├─ deduplicateReadEvents()
├─ capturePathState() → post-build PATH snapshot
├─ computeTrust() → Trust Score 0-100
├─ buildIntentFlow() → flujo esperado vs observado
├─ loadContract() / updateContract() / saveContract()
├─ findEphemeralProcesses() → <100ms
├─ Anomalías:
│ ├─ dangerousSeen → curl/wget/perl/etc
│ ├─ unknown processes
│ ├─ suspicious network → pastebin, .ru, .cn
│ ├─ intent deviations
│ ├─ contract violations
│ └─ ephemeral processes
└─ BuildRecord completo
POLL_PROCESS_MS = 200POLL_FILE_MS = 1000POLL_NET_MS = 2000POLL_READ_MS = 1500
interface BuildRecord {
command: string
args: string[]
cwd: string
startTime: string
durationMs: number
exitCode: number | null
platform: string
nodeVersion: string
env: Record<string, string>
processes: BuildProcessEvent[]
files: BuildFileEvent[]
network: BuildNetEvent[]
artifactHashes: ArtifactHash[]
summary: BuildSummary
hashChain: BuildChainLink[]
identity?: BuildIdentity
readFiles?: FileReadEvent[]
inputIdentity?: BuildInputIdentity
scriptIdentities?: ScriptIdentity[]
pathResolutions?: PathState[]
trustResult?: TrustResult
buildIntent?: BuildIntentStep[]
buildContractViolations?: BuildContractViolation[]
secretFlow?: SecretFlow
compilerInvocations?: CompilerInvocationIdentity
hermetricScore?: number
reproducibilityScore?: number
}Archivo: src/core/network/file-read-provenance.ts (209 líneas)
Tests: file-read-provenance.test.ts (16 tests)
Propósito: Rastrear qué archivos lee cada proceso del build.
3 estrategias:
- Pre-build inventory (
capturePreBuildInventory): Camina el directorio, captura size + mtimeMs de cada archivo (excluye.,node_modules,.git). - Poll en vivo (
pollProcessOpenFiles): Por cada PID en el build:- Linux:
/proc/<pid>/fd/→readlink()→ filtra por cwd - Windows:
Get-Process -Id <pid> | Select-Object Modules+Handle
- Linux:
- Post-build detection (
detectReadFilesPostBuild): Compara mtime post-build vs pre-build inventory. Asigna el proceso más cercano temporalmente como responsable.
Deduplicación: Clave compuesta filePath:pid.
Rendering: Agrupa por proceso, muestra hasta 5 archivos por proceso, máximo 20 líneas.
Integración: pollReads cada 1500ms en recorder. readFiles se almacena en BuildRecord.readFiles. Aristas read en provenance-graph.ts:addReadEdges().
Archivo: src/core/network/build-input-identity.ts (353 líneas)
Tests: build-input-identity.test.ts (42 tests)
Propósito: Identificar, categorizar y fingerprintear todos los archivos de entrada del build.
Categorías:
build_system: Makefile, CMakeLists.txt, meson.build, SConstruct, build.ninja, WORKSPACE, BUILD.bazel (12 patrones)language_config: Cargo.toml, go.mod, package.json, pom.xml, build.gradle, pyproject.toml, requirements.txt, etc. (22 patrones)ci_config:.github/workflows/,.gitlab-ci.yml, azure-pipelines.yml, .circleci/, Jenkinsfile, .woodpecker/, .drone.yml (8 patrones)shell_script:.sh,.ps1,.bat,.cmd,.bash,.zsh,.fish- Excluye: README*, LICENSE*, .gitignore, node_modules/, .git/, docs/, etc.
Input fingerprint: SHA256 determinístico de todos los inputs ordenados por path → ${path}:${sha256}:${size}:${permissions}\n
Diff: Detecta new, removed, modified, permission_changed, owner_changed, symlink_changed.
Input Stability: (current/previous)*100 — porcentaje de inputs mantenidos vs baseline.
Script Identity: Captura intérprete + SHA256 + realpath + imports extraídos por lenguaje (Python: import/from, JS/TS: require/import, Shell: source/.).
Toolchain Purity: expectedOnly / max(expected.length, 1) * 100.
Archivo: src/core/network/path-resolution.ts (197 líneas)
Propósito: Capturar estado completo de PATH + resolución de 22 herramientas de compilación.
Herramientas resueltas:
- Compiladores: gcc, g++, clang, clang++, cc, c++
- Linkers/binutils: ld, ld.lld, ar, ranlib, strip, objcopy, nm, readelf
- Build systems: make, cmake, ninja, meson
- Runtimes: python, python3, node, rustc, cargo, go, javac, java
- Network: curl, wget, git
Por cada tool: which → realpath → fs.readFileSync + SHA256 → --version
Diff entre dos estados:
reordered(warning): Misma longitud, entries cambiaron de posiciónprepended(critical): Entry nueva al inicioappended(warning): Entry nueva al finalremoved(warning): Entry eliminadashadowed(critical): Misma tool resuelve a distinto realpath
Integración: capturePathState() pre-build + post-build. Se guarda como pathResolutions: PathState[].
Archivo: src/core/network/build-trust-engine.ts (279 líneas)
Propósito: Ponderar 7 dimensiones de confianza → score 0-100 completamente explicable.
| Dimensión | Peso | Score base | Penalizaciones |
|---|---|---|---|
| toolchain_identity | 0.18 | 100 | -20 sin toolchain, -15/SHA cambiado |
| input_identity | 0.18 | 100 | -8/modified, -5/new, -5/removed (máx -40) |
| artifact_integrity | 0.15 | 100 | -10/artifact cambiado (máx -40) |
| behavior | 0.15 | 100 | -15/anomalía (máx -45) |
| network | 0.12 | 100 | -5/conn (máx -25), -25 destinos sospechosos |
| graph | 0.12 | 100 | -5/unrecognized process (máx -20) |
| trend | 0.10 | 80 | +10 hash links, +10 duración estable, -10 cambio >50% |
Score global: round(sum(score_i * weight_i))
Métricas adicionales:
inputStability: delega aBuildInputIdentity.inputStabilitytoolchainPurity: % de herramientas esperadas que están presentesbuildDeterminism: boolean — mismos inputs + mismos artifacts = true
Rendering: Barra visual █░ por dimensión + evidencia textual con +N/-N.
Archivo: src/core/network/process-lifetime.ts (88 líneas)
Tests: build-lifetime.test.ts (21 tests)
Propósito: Trackear startTime y exitTime por proceso; detectar efímeros <100ms.
Mecanismo: trackProcessExits() compara currentPids vs previousPollPids. PIDs desaparecidos se marcan con exitTime = now.
Efímeros: findEphemeralProcesses(thresholdMs=100) — procesos con duración positiva <100ms. Se reportan como anomalía.
Archivo: src/core/network/build-intent.ts (148 líneas)
Propósito: Clasificar cada proceso en 10 intenciones + comparar flujo observado vs esperado.
Intenciones (ProcessIntent):
configure | compile | link | archive | download | script | package | test | install | unknown
Mapeo de 30+ herramientas: gcc/clang → compile, ld → link, curl/wget → download, make/ninja/cargo → package, npm/pip → install, etc.
Orden canónico esperado: configure → compile → archive → link → download → package → script → test → install
Desviaciones detectadas:
- Orden inesperado: etapa posterior antes que anterior
- Etapa no esperada: observada pero no en flujo esperado
- Etapa esperada faltante: si hay build_system inputs, configure es esperado
Archivo: src/core/network/build-contract.ts (116 líneas)
Propósito: Persistir herramientas vistas por proyecto; detectar herramientas nuevas como violaciones.
Almacenamiento: ~/.sentinel/builds/contracts/<command>_<cwd_sanitized>.json
Violaciones:
- Tool nueva (warning): Aparece después de
threshold=5builds - Tool faltante (info): Tool con count≥threshold no aparece en este build
- Downloader temprano (critical): curl/wget/fetch con count < threshold (proyecto joven descargando)
Archivo: src/core/network/secret-flow.ts (187 líneas)
Propósito: Rastrear qué secretos lee cada proceso del build y si esos secretos terminan en conexiones de red (exfiltration risk).
Detección de secretos (20 patrones):
sk_live_,sk_test_,pk_live_,pk_test_(Stripe)ghp_,gho_,ghu_,ghs_,ghr_(GitHub tokens)AKIA+ 20 chars (AWS access key)-----BEGIN (RSA|EC|OPENSSH|DSA) PRIVATE KEY-----eyJ(JWT),mongodb.*:([^@]+)@(Mongo connection strings)xox[abposr]-(Slack tokens),AIza[0-9A-Za-z_-](GCP API)sq0atp-,sq0csp-(Square),sk-[0-9a-fA-F]{32,}(OpenAI)pk-[0-9a-fA-F]{32,},da2-[a-z0-9]{26}(AppSync)ghr_[0-9a-zA-Z]{35,},npm_[a-zA-Z0-9]{36},pat-[a-zA-Z0-9]{22,}pypi-[A-Za-z0-9],rubygems_[A-Za-z0-9]
Detección por archivos sensibles (por nombre):
.env*,credentials,.netrc,*.pem,*.key,id_rsa,id_dsa,id_ed25519,id_ecdsakubeconfig,service-account*,secrets.yml,tokens,oauth*
Detección por variables de entorno (38 vars):
AWS_SECRET_*,AWS_SESSION_TOKEN,AZURE_*,GCP_PROJECT,GOOGLE_*GITHUB_TOKEN,GH_TOKEN,GITLAB_*,BITBUCKET_*STRIPE_*,SLACK_*,DOCKER_*,NPM_TOKEN,PYPI_TOKENDATABASE_URL,REDIS_URL,MONGODB_URI,PG*,MYSQL_*,MONGO_*
Chain de Exfiltración:
buildSecretFlowChains(): Procesa todas las lecturas de archivos sensibles + variables de entorno del build, detecta si algún proceso que leyó secretos también hizo conexiones de red, asigna severidad (CRITICAL si exfiltración detectada, HIGH si hay secretos críticos, MEDIUM si hay secretos).
API pública:
scanFileForSecrets(filePath, content)— escanea contenido de archivo contra 20 patrones, retornaSecretAccess[]scanProcessReadsForSecrets(readEvents)— cruza eventos de lectura de archivos contra archivos sensibles por nombrescanEnvForSecrets()— escaneaprocess.envcontra 38 vars sensibles, retornaSecretAccess[]buildSecretFlowChains(processes, netEvents, fileReadEvents)— construye cadenas secreto → PID → redcomputeHermeticScore(netConns, unknownTools, pathChanges, buildSteps, ...)— métrica 0-100 de hermeticidadcomputeReproducibilityScore(...)— métrica 0-100 de reproducibilidadrenderSecretFlowChains(chains)— renderizado ASCII de cadenas de secretosrenderHermeticScore(score)— renderizado ASCII con barra visualrenderReproducibilityScore(result)— renderizado ASCII con check/x
Implementado en: src/core/network/secret-flow.ts (como parte de Secret Flow)
Fórmula: score = 100 - sum(penalizaciones)
Penalizaciones (cada una tiene un máximo):
| Factor | Penalización base | Máx |
|---|---|---|
| Conexiones de red | min(count * 8, 30) |
30 |
| Herramientas desconocidas | min(count * 5, 20) |
20 |
| Cambios de PATH | min(count * 10, 20) |
20 |
| Etapas inesperadas | 5 c/u | 20 |
| Contract violations | 10 c/u | 40 |
| Build ephemeral (sin baseline) | 15 | 15 |
| Secretos encontrados | 8 c/u | 30 |
Interpretación:
- 90–100: Hermetic ✅
- 70–89: Mostly hermetic
- 50–69: Partial hermetic
- <50: Non-hermetic
⚠️
Archivo: src/core/network/compiler-invocation.ts (171 líneas)
Propósito: Capturar el argv exacto + response files + environment de cada invocación de compilador/linker para identificar builds, detectar flags sospechosos y permitir reproducibilidad.
Compiladores detectados (14):
gcc, g++, cc, c++, clang, clang++, cc1, cc1plus, collect2, rustc, go, javac, kotlinc, ghc
Linkers detectados (5):
ld, ld.lld, ld.gold, lld-link, link
Flags sospechosos detectados (en argv y response files):
-fno-stack-protector,-z execstack: deshabilitar protecciones de seguridad-fplugin,-fplugin-arg-*: plugins de compilador (potencialmente maliciosos)-Wl,--enable-new-dtags,-Wl,-rpath: manipulación de RPATH--no-crypto,--no-verify: deshabilitar verificación criptográfica (en rustc/go)
API pública:
isCompilerOrLinker(name)— detecta si un nombre de proceso es compilador/linker conocidoextractCompilerInvocation(process, cwd)— parsea argv completo, extrae inputFiles, outputFiles, flags, defines, includeDirs, responseFiles con contenido, envSnapshotanalyzeCompilerInvocations(processes)— agrega todas las invocaciones de compiladores del build, detecta invocaciones sospechosasrenderCompilerInvocations(invocations)— renderizado ASCII de invocaciones
Archivo: src/core/network/build-identity.ts (173 líneas)
Propósito: Capturar identidad completa del entorno de compilación.
Campos:
- Hostname, platform, arch, kernel, kernelVersion, OS version
- Container detection (Docker, containerd, kubepods)
- CI provider detection: GitHub Actions, GitLab CI, Jenkins, CircleCI, Azure DevOps
- toolVersions: 15 herramientas con
--version - toolIdentities: 15 herramientas con
which→realpath→ SHA256 → size → mtime - builderProcess, cpus, memoryGb, uptimeHours
Archivo: src/core/network/build-dna.ts (369 líneas)
Build DNA: Fingerprint de una build con 7 dimensiones:
toolchain: uniqueProcesses ordenadosenvVector: environment keys ordenadosartifactHashes: SHA256 de artifacts ordenadosprocessGraphSignature: SHA256 del árbol de procesos serializadonetworkProfile: count TCP + hosts ordenadostotalFileOps: created + modified + deleteddurationMs,anomalyCount
Multi-DNA: 6 fingerprints cortos (SHA256 truncados a 16 chars):
toolchain,environment,artifact,network,graph,behavior
Similaridad:
computeDnaSimilarity(): Jaccard + numericSim con pesos [0.25, 0.25, 0.2, 0.1, 0.1, 0.05, 0.05]computeMultiDnaSimilarity(): Coincidencia exacta con pesos [0.2, 0.1, 0.25, 0.1, 0.2, 0.15]
Build Graph Edges: inferBuildGraph() produce aristas spawned, produced desde procesos y archivos.
Behavior Chain: deriveBehaviorChain() — secuencia de comportamientos observados (configure → compile → link → ...).
Archivo: src/core/network/provenance-graph.ts (285 líneas)
Propósito: Construir un grafo de procedencia del build con nodos (tool, source, intermediate, artifact) y aristas (compiled, linked, archived, generated, downloaded, configured, read).
Nodos:
tool: gcc, ld, make, etc.source: .c, .cc, .cpp, .h, .rs, .go, .java, .ts, .js, .py, .s, .S, .asmintermediate: .o, .obj, .lo, .lib, .a, .rlib, .classartifact: .so, .dll, .exe, .out, .wasm, .node, .dylib, .bin, .elf
Aristas: Inferidas por proximidad temporal entre procesos y archivos creados.
Stages: configure → compile → archive → link → download → package → finalize
Read edges: addReadEdges() conecta tools con archivos leídos vía FileReadEvent[].
Archivo: src/core/network/build-baseline.ts (121 líneas)
Propósito: Mantener baseline estadístico de builds para detectar outliers.
Baseline stats: mean + std de duration, artifact count, file ops. Typical toolchain (tools presentes en ≥80% de builds). Typical graph signature.
Normality (z-score): z = (value - mean) / std. Outlier si |z| > 3 en cualquier dimensión.
Almacenamiento: ~/.sentinel/baselines/<command>_<cwd>.json (máx 50 builds).
Archivo: src/core/network/trend-engine.ts (197 líneas)
Propósito: Análisis de tendencias históricas con EWMA, CUSUM y slope.
Métricas trackeadas (10): duration_ms, process_count, artifact_count, file_creations, file_modifications, file_deletions, file_ops_total, network_connections, tool_count, unique_processes.
Algoritmos:
- Slope: regresión lineal simple sobre índices
- EWMA: suavizado exponencial con α=0.3
- CUSUM: detección de cambio acumulado con k=0.5σ
Drift: none (<3%), low (<10%), medium (<20%), high (≥20%). Alert si high o medium con CUSUM > 3σ.
Almacenamiento: ~/.sentinel/builds/<command>_<cwd>/<timestamp>.json (máx 200 builds).
Archivo: src/core/network/build-explain.ts (374 líneas)
Propósito: Explicar por qué un build difiere del anterior o del release baseline.
Eventos detectados:
process_spawn: procesos nuevos (con severidad según peligrosidad)file_create: archivos nuevosartifact_hash: artifacts con SHA256 cambiado (severidad high)network_conn: conexiones TCP nuevasdns_query: DNS queries nuevas
Causal chain: Construye cadena causal desde el evento raíz (primero en timeline o primer high severity).
Confidence breakdown: toolchain, environment, artifact, network, graph, behavior con pesos.
Archivo: src/core/network/evidence-chain.ts (285 líneas)
Propósito: Correlacionar eventos en cadenas de evidencia (exfiltration, preparation, snapshot, embedding).
Cadenas detectadas:
- Exfiltration chain: file reads → git bundle → network upload
- Preparation chain: git commands → system preparation
- Snapshot chain: mass file reads → git history → full repo snapshot
- Embedding chain: file reads → AI embeddings
Archivo: src/core/network/risk-engine.ts (269 líneas)
Propósito: Evaluar riesgo basado en comportamientos observados con pesos y multiplicadores.
Behavior weights (37 tipos): desde repo_indexed (25) hasta canary_exfiltrated (99).
Multiplicadores:
bundle_plus_upload: 2.0xsecret_plus_exfil: 2.5xanti_evasion_plus_exfil: 2.5xcanary_triggered: 3.0xmultiple_behaviors: 1.3x- Temporal: 1.1x–1.5x según avg gap entre comportamientos
Score normalizado 0-100. Thresholds: CRITICAL ≥80, HIGH ≥50, MEDIUM ≥20, LOW <20.
Archivo: src/core/network/anti-evasion-engine.ts (437 líneas)
Propósito: Detectar técnicas de evasión de monitoreo.
Señales (9 tipos):
artificial_rhythm,fragmented_traffic,protocol_hopping,custom_compressionmonitor_awareness,memory_only_ops,distributed_chain,no_temp_files,preparation_phase,process_chain
Pesos: 15–40 según severidad.
Archivo: src/core/network/behavior-engine.ts
Propósito: Clasificar eventos individuales (flows, procesos, file accesses, git commands) en comportamientos.
Comportamientos clasificados:
- Flows: AI prompts, code upload, suspicious connections
- Procesos: monitor disable, preparation commands
- File accesses: mass read, embedding, secrets scan
- Git: bundle, archive, history read, objects read, push
Archivo: src/core/network/canary-system.ts (235 líneas)
Propósito: Sistema de señuelos (decoy files + fake secrets) para detectar exfiltración.
15 decoy files: customer_data_2026_q2.csv, api_keys_internal.json, aws_credentials_prod.json, etc.
14 fake secret patterns: sk-live-, ghp_, AKIA, -----BEGIN RSA PRIVATE KEY-----, etc.
Marker: SENTINEL_CANARY_TOKEN_ en contenido de señuelos.
Archivo: src/core/network/native-events.ts (129 líneas)
Propósito: Abstracción unificada para recibir eventos del sistema operativo en tiempo real, reemplazando el polling periódico de procesos.
Interfaz común (NativeEventProvider):
interface NativeEventProvider {
name: string
start(): void
stop(): void
onProcessEvent(callback): void
onFileEvent(callback): void
onNetEvent(callback): void
}Implementaciones:
| Provider | OS | Mecanismo | Funcionalidad |
|---|---|---|---|
EtwEventProvider |
Windows | ETW via wevtutil + Get-WinEvent |
ProcessStart, ProcessStop, TcpIp (connect/disconnect) |
EBpfEventProvider |
Linux | eBPF via execsnoop/opensnoop/tcptracer (BCC) |
Process exec, File open, TCP connect |
PollingEventProvider |
Any | ps/Get-CimInstance a intervalos |
Procesos, archivos, red |
Factory: createEventProvider() selecciona automáticamente: ETW en Windows, eBPF en Linux (si BCC disponible), polling como fallback.
Archivo: src/core/network/pipeline.ts (499 líneas)
Propósito: Orquestar el pipeline completo de auditoría de red.
Flujo completo:
- Recibir eventos (flows, procesos, file accesses, git)
- Clasificar cada evento en comportamientos
- Anti-evasion signals
- Preparation detection
- Process chain correlation
- Logical correlation rules (bundle+upload → upload, canary+exfil → exfiltrated)
- Risk assessment
- Build evidence chain + verify
- MITRE ATT&CK mapping
- Behavior timeline (Preparation → Collection → Packaging → Exfiltration)
- Build verdict (session DNA + confidence score)
Archivo: src/cli/main.ts
| Comando | Descripción |
|---|---|
scan [path] |
SAST scan con múltiples formatos de salida |
build run <command> |
Build Flight Recorder + Trust Report |
build explain [id] |
Explicar diferencias entre builds |
build mark-release <id> |
Marcar build como release baseline |
build release |
Mostrar release actual |
explain [paths] |
Explicar hallazgos de seguridad |
history [path] |
Risk history + trends |
graph history/diff |
Graph snapshot management |
verify-pkg <package> |
Supply chain audit |
audit-deps |
Dependency audit (lockfile, OSV, provenance) |
deps-tree |
Transitive dependency scan |
trust-cache |
Trust cache management |
permissions [package] |
Capability audit |
policy |
Policy management |
baseline create/diff |
System baseline |
doctor |
System health check |
drift <pkg> <ver> <path> |
Behavioral drift |
benchmark |
FP/FN corpus benchmark |
integrity |
Sentinel self-integrity check |
Archivo: src/core/network/build-types.ts (525 líneas)
BuildRecord,BuildProcessEvent,BuildFileEvent,BuildNetEventBuildSummary,BuildChainLink,ArtifactHash,BuildStepBuildIdentity,ToolIdentity
BuildInputCategory,BuildInput,BuildInputIdentity,InputChange,ScriptIdentity
ToolResolution,PathState
TrustDimension,TrustResult
ProcessIntent,BuildIntentStep,BuildIntentFlow
BuildContractEntry,BuildContractViolation
FileReadEvent
SecretAccess,SecretFlowChain,SecretFlow
CompilerInvocation,CompilerInvocationIdentity
BuildDna,MultiDna,MultiDnaSimilarity,BuildExplanation,ConfidenceBreakdownCausalNode,BuildGraphEdge,ExplainEvent,ExplainResult
ProvenanceNode,ProvenanceEdge,ProvenanceGraph,ProvenanceNodeType
BuildBaselineStats,BuildNormalityResult
TrendMetric,TrendResult
BUILD_TOOLS(40+ tools),DANGEROUS_BUILD_TOOLS(10+),BUILD_ENV_KEYS(18 keys)
Framework: Vitest (v4.1.7)
Config: vitest.config.ts con testTimeout: 30000
Ubicación: src/**/*.test.ts y src/**/*.spec.ts
Tests del Build Trust:
| Archivo | Tests |
|---|---|
file-read-provenance.test.ts |
16 |
build-input-identity.test.ts |
42 |
build-lifetime.test.ts |
21 |
secret-flow.test.ts |
23 |
compiler-invocation.test.ts |
10 |
evidence-reliability.test.ts |
16 |
evasion-detection.test.ts |
10 |
build-serializer.test.ts |
7 |
build-explain.test.ts |
— |
build-adv-features.test.ts |
— |
Total suite: 1040 tests, 47 test files, 0 errores.
Variables de entorno relevantes:
PATH: capturado pre/post buildNODE_OPTIONS:--no-deprecationpara el proceso hijoDOCKER_IMAGE,IMAGE_NAME,CONTAINER_ID: detección de contenedor- Envars CI:
GITHUB_*,GITLAB_*,JENKINS_*,CIRCLE_*,TF_BUILD
Archivos de almacenamiento:
| Propósito | Ruta |
|---|---|
| Build records | ~/.sentinel/builds/<key>/<timestamp>.json |
| Baselines | ~/.sentinel/baselines/<key>.json |
| Contracts | ~/.sentinel/builds/contracts/<key>.json |
| Release store | ~/.sentinel/builds/<key>_release.json |
| Session history | ~/.sentinel/history/ |
| Graph history | ~/.sentinel/graphs/ |
Políticas: ~/.sentinel/policy.json — ci-mode, fail-closed, quarantine.
Archivo: scripts/validate-projects.ts (150 líneas)
Propósito: Framework para validar Sentinel contra proyectos reales, midiendo FP/FN y asegurando que las detecciones sean correctas.
Proyectos validados:
| Proyecto | Lenguaje | Build system | Expected tools |
|---|---|---|---|
hello-node |
JavaScript | node/npm | node, npm |
libsodium |
C | autotools | gcc/cc, ld, libtool, make |
busybox |
C | make | gcc/cc, ld, make, gzip |
redis |
C | make | gcc/cc, ld, make |
Flujo de validación:
git clone --depth=1en~/.sentinel/validation-sources/- Ejecuta
recordBuild()para cada proyecto - Reporta hermetic score, trust score, tools esperados vs detectados, artifacts esperados vs encontrados
- Detecta anomalías (tools faltantes, artifacts extra, etc.)
- Guarda resultados en
~/.sentinel/validation-results/
Archivo: src/core/network/evidence-reliability.ts (155 líneas)
Tests: evidence-reliability.test.ts (16 tests)
Propósito: Asignar nivel de confianza a cada señal de evidencia según su fuente, permitiendo que el Trust Score no solo mida el build, sino también la calidad de la evidencia.
| Fuente | Confianza | Plataforma |
|---|---|---|
| ETW | 98 | Windows |
| eBPF | 97 | Linux |
| Endpoint Security | 96 | macOS |
| auditd | 95 | Linux |
| ftrace | 94 | Linux |
| procfs | 85 | Linux |
| CIM Query | 82 | Windows |
| Handle API | 78 | Windows |
| ps | 72 | Linux/Unix |
| Polling | 65 | Any |
| mtime heuristic | 42 | Any |
evidenceConfidence(source)— retorna confianza para una fuenteprocessConfidence(proc)— computa confianza de un proceso (source + start/exit times)fileReadConfidence(event)— computa confianza de lectura de archivonetEventConfidence(event)— computa confianza de evento de redfileEventConfidence(event)— computa confianza de evento de archivocomputeObservationConfidence(processes, files, network, reads)— agrega todo en unObservationConfidencecon overall, signals, coverage, sources, weakest/strongest signalrenderObservationConfidence(oc)— renderizado ASCII
interface ObservationConfidence {
overall: number // 0-100
signals: Record<string, number> // por tipo de señal
coverage: number // % de señales con confianza ≥50
sources: EvidenceSource[]
weakestSignal: number
strongestSignal: number
}Uso: Se almacena en BuildRecord.observationConfidence. Permite distinguir entre:
- "Trust 93 + Observation Confidence 91" → evidencia sólida
- "Trust 93 + Observation Confidence 41" → build parece confiable pero la evidencia es débil
Archivo: src/core/network/evasion-detection.ts (243 líneas)
Tests: evasion-detection.test.ts (10 tests)
Propósito: Detectar técnicas de evasión de monitoreo, cubriendo 10 vectores de ataque identificados en threat model.
| # | Vector | Detección | Severidad |
|---|---|---|---|
| 1 | Compilación desde stdin | CompilerInvocation.fromStdin |
MEDIUM |
| 2 | Ejecución vía memfd_create | CompilerInvocation.fromMemfd + detectFilelessExecution() |
HIGH |
| 3 | Plugin LLVM desde memoria | captureProcessMaps() → regiones anónimas rwx |
CRITICAL |
| 4 | Procesos <25ms (evaden polling) | classifyEphemeralProcesses() con threshold 25ms |
HIGH |
| 5 | Response file cambia post-lectura | detectResponseFileChanges() en compiler-invocation |
HIGH |
| 6 | Wrapper renombrado como gcc | ToolIdentity.realPath + SHA256 en build-identity |
MEDIUM |
| 7 | LD_PRELOAD post-snapshot | captureProcessMaps() → /proc/*/maps + environ |
HIGH |
| 8 | DNS-over-HTTPS (bypass DNS audit) | detectDnsOverHttps() contra 15+ proveedores conocidos |
MEDIUM |
| 9 | Named pipes (IPC no trackeado) | detectNamedPipes() vía \\.\\pipe\\ o FIFOs |
MEDIUM |
| 10 | ptrace injection (procesos sin padre) | detectOrphanProcesses() → ppid no está en build set |
CRITICAL |
detectOrphanProcesses(processes)— detecta procesos cuyo ppid no está en el setdetectNamedPipes(processes, cwd)— enumera named pipes/fifos activosdetectDnsOverHttps(netEvents)— detecta conexiones a DoH providerscaptureProcessMaps(processes)— lee/proc/*/maps, detecta regiones rwx/memfd, captura LD_PRELOADdetectEphemeralProcesses(processes, thresholds)— clustering multithresholddetectFilelessExecution(processes)— detecta /dev/fd, pipe:[], socket:[] en cmdlineinferEvidenceSource(platform, options?)— selecciona mejor fuente disponible
Archivo: src/core/network/build-serializer.ts (120 líneas)
Tests: build-serializer.test.ts (7 tests)
Propósito: Serialización eficiente de BuildRecord para builds grandes, con truncamiento inteligente de arrays y strings.
| Campo | Máximo |
|---|---|
| Procesos | 500 |
| Archivos | 2000 |
| Eventos de red | 500 |
| Reads | 500 |
| Hash chain | 100 |
| Anomalías | 200 |
| Artefactos | 200 |
| Compiler invocations | 50 |
| Secretos | 100 |
| Cadenas de secretos | 50 |
| String length | 500 chars |
compactRecord(record)— retorna BuildRecord truncado con reporte de serializaciónrenderSerializationReport(report)— renderizado ASCII con bytes original/compactado/ahorrado
- Seasonality en Trend Engine (weekly/release/CI cycles)
- Dependency Provenance (crates, hashes, mirrors, registry, lockfile)
- Validación contra Linux kernel, LLVM, OpenSSL, PostgreSQL
- Evidence Reliability Engine v2: Confidence scoring dinámico basado en consistencia cross-source
- Evasion Detection v2: Detección de time-of-check/time-of-use (TOCTOU) en response files y PATH
- macOS Endpoint Security: Implementar
NativeEventProviderpara ESF - eBPF real-time: Migrar EBpfEventProvider de polling ps a suscripción BCC/libbpf
- Build Story narrative generada por LLM
- Trust Score exportable como SBOM (CycloneDX)
- FP/FN corpus automation con benchmark CI
- Modo "air-gapped": operación sin conexión a Internet