Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Documentation aligned with implementation: README/SPECIFICATION/SECURITY now describe the actual `--network=none` sandbox (previously claimed an isolated bridge); Python audit hook list corrected (`compile`/`exec`/`import` — `eval` is a Node.js-only event); SPECIFICATION test-data section rewritten around `probe-alpha`/`probe-npm`/`evasion-test` (the obsolete `axios-demo` entry was stale)

### Fixed
- **`/.dockerenv` masking actually works now** — the post-start `rm -f /.dockerenv` had been silently failing on every scan since `--read-only` rootfs was introduced (the rootfs is, by design, not writable). `/.dockerenv` is now masked at container creation time by bind-mounting an empty regular file from the host over the path. Sandbox-aware payloads that read `/.dockerenv` see empty content; gVisor (`--runtime=runsc`) is still recommended to also defeat path-existence checks
- **Sandbox preparation no longer fails silently** — `plantHoneypotFiles`, `restoreLocalBin`, `WriteProbeScripts`, and `WriteProbeScriptsMulti` now return errors instead of swallowing the result of every `docker exec`. A swallowed honeypot-write or probe-script-write failure used to leave the container partially prepared, and any sandbox-aware payload that detected the gap and stayed dormant would surface as `clean`. The errors propagate through `Start` / `StartPaused` and abort the scan
- 21 linter errors: gofmt (15 files), importShadow (2), ifElseChain (1), godot (1), intrange (1), staticcheck De Morgan (1)

## [0.5.0]
Expand Down
2 changes: 1 addition & 1 deletion SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ kojuto is a security tool that intentionally runs untrusted code in an isolated
### Anti-Fingerprinting

- Host hostname, username, CPU count, and memory are mirrored into the container
- `/.dockerenv` is removed on startup
- `/.dockerenv` is masked at container creation time by bind-mounting an empty regular file from the host over it (`--read-only` rootfs makes post-start `rm` impossible, so masking is the only mechanism)
- Package mount path mirrors host directory layout
- `/etc/resolv.conf` is populated via `--dns=198.51.100.1` (RFC 5737 TEST-NET-2, guaranteed unreachable) so the file is non-empty even under `--network=none` — prevents the empty-resolv-conf signal that would reveal isolation. `connect()` returns `ENETUNREACH`; combine with `--runtime runsc` to mask remaining `/proc/1/cgroup` and `/proc/self/mountinfo` signals

Expand Down
13 changes: 10 additions & 3 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -434,7 +434,9 @@ func runBatchScreening(deps []depfile.Dep, ecosystem string) (string, error) {
}

// Import all packages under simulated OS identities (3 scripts total).
sb.WriteProbeScriptsMulti(ctx, pkgNames)
if err := sb.WriteProbeScriptsMulti(ctx, pkgNames); err != nil {
return "", fmt.Errorf("writing batch probe scripts: %w", err)
}
importCmds := sb.ImportCommandsMulti(pkgNames)
osNames := []string{"Linux", "Windows", "macOS"}

Expand Down Expand Up @@ -966,7 +968,10 @@ func runEBPFProbe(ctx context.Context, sb *sandbox.Sandbox, _ string) (*scanResu
// platform-gated payloads. Without this, eBPF mode misses every
// __init__.py-resident attack — most pypi malware lives here, not
// in setup.py.
sb.WriteProbeScripts(ctx)
if err := sb.WriteProbeScripts(ctx); err != nil {
_ = ep.Close()
return nil, fmt.Errorf("writing probe scripts: %w", err)
}
importCmds := sb.ImportCommands()
osNames := []string{"Linux", "Windows", "macOS"}
for i, cmd := range importCmds {
Expand Down Expand Up @@ -1045,7 +1050,9 @@ func runContainerStraceProbe(ctx context.Context, sb *sandbox.Sandbox, _ string)

// Phase 2: Import under each simulated OS to defeat platform-gated payloads.
// Write probe scripts to /tmp first (outside strace), then execute them.
sb.WriteProbeScripts(ctx)
if err := sb.WriteProbeScripts(ctx); err != nil {
return nil, fmt.Errorf("writing probe scripts: %w", err)
}

importCmds := sb.ImportCommands()
osNames := []string{"Linux", "Windows", "macOS"}
Expand Down
Loading
Loading