mirror of
https://github.com/safedep/pmg.git
synced 2026-08-03 07:24:09 +02:00
feat: Add Sandbox Inspection and Debugging Commands (#261)
* feat: add sandbox DX commands * fix: Linter errors * fix: Sandbox deny log parsing * fix: Sandbox docs * refactor: Maintain SSOT across pkg dependencies * fix: Linter errors
This commit is contained in:
@@ -76,3 +76,113 @@ func TestExtractSeatbeltViolations(t *testing.T) {
|
||||
assert.Equal(t, "/tmp/.env", violations[0].RuleTarget)
|
||||
assert.Equal(t, "node", violations[0].Process)
|
||||
}
|
||||
|
||||
// Regression: a denial that hits the catch-all `(deny default ...)` rule
|
||||
// must still be classified as the typed kind (and get a typed label) by
|
||||
// recovering the verb from the sandbox-exec preamble. Without this, the
|
||||
// actual failure-causing operation (e.g. a write) gets buried below
|
||||
// incidental typed reads in primaryViolation ranking.
|
||||
func TestExtractSeatbeltViolationsRecoversTypedKindFromDefaultDeny(t *testing.T) {
|
||||
entries := []seatbeltLogEntry{
|
||||
{
|
||||
EventMessage: `Sandbox: node(59437) deny(1) file-write-data /Users/dev/project/.astro/types.d.ts ` +
|
||||
seatbeltLogMessage("run-1", "default", ""),
|
||||
Process: "node",
|
||||
},
|
||||
{
|
||||
EventMessage: `Sandbox: node(59438) deny(1) network-outbound 1.2.3.4:443 ` +
|
||||
seatbeltLogMessage("run-1", "default", ""),
|
||||
Process: "node",
|
||||
},
|
||||
}
|
||||
|
||||
violations := extractSeatbeltViolations(entries, "run-1")
|
||||
require.Len(t, violations, 2)
|
||||
|
||||
// File write hit the catch-all deny — Kind is recovered, RawKind stays
|
||||
// "default" to preserve the "catch-all match" signal, and the label
|
||||
// reflects the recovered kind so users see "write access denied".
|
||||
assert.Equal(t, sandbox.ViolationKindFSWrite, violations[0].Kind)
|
||||
assert.Equal(t, "default", violations[0].RawKind)
|
||||
assert.Equal(t, "/Users/dev/project/.astro/types.d.ts", violations[0].Target)
|
||||
assert.Equal(t, "write access denied: /Users/dev/project/.astro/types.d.ts", violations[0].RuleLabel)
|
||||
|
||||
// Unrecognized verb (network-outbound) stays generic — no over-claiming.
|
||||
assert.Equal(t, sandbox.ViolationKindGenericDeny, violations[1].Kind)
|
||||
assert.Equal(t, "default", violations[1].RawKind)
|
||||
}
|
||||
|
||||
func TestInferSeatbeltKindFromRawLog(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
raw string
|
||||
wantKind sandbox.ViolationKind
|
||||
wantLabel string
|
||||
wantOK bool
|
||||
}{
|
||||
{
|
||||
name: "file-write-data maps to fs_write",
|
||||
raw: `Sandbox: node(1) deny(1) file-write-data /tmp/x`,
|
||||
wantKind: sandbox.ViolationKindFSWrite,
|
||||
wantLabel: "file-write",
|
||||
wantOK: true,
|
||||
},
|
||||
{
|
||||
name: "file-write-unlink maps to fs_delete_or_rename",
|
||||
raw: `Sandbox: node(1) deny(1) file-write-unlink /tmp/x`,
|
||||
wantKind: sandbox.ViolationKindFSDeleteOrRename,
|
||||
wantLabel: "file-write-unlink",
|
||||
wantOK: true,
|
||||
},
|
||||
{
|
||||
name: "file-rename maps to fs_delete_or_rename",
|
||||
raw: `Sandbox: node(1) deny(1) file-rename /tmp/a`,
|
||||
wantKind: sandbox.ViolationKindFSDeleteOrRename,
|
||||
wantLabel: "file-write-unlink",
|
||||
wantOK: true,
|
||||
},
|
||||
{
|
||||
name: "file-read-metadata maps to fs_read",
|
||||
raw: `Sandbox: node(1) deny(1) file-read-metadata /tmp/x`,
|
||||
wantKind: sandbox.ViolationKindFSRead,
|
||||
wantLabel: "file-read",
|
||||
wantOK: true,
|
||||
},
|
||||
{
|
||||
name: "process-exec maps to exec",
|
||||
raw: `Sandbox: node(1) deny(1) process-exec /usr/bin/curl`,
|
||||
wantKind: sandbox.ViolationKindExec,
|
||||
wantLabel: "process-exec",
|
||||
wantOK: true,
|
||||
},
|
||||
{
|
||||
name: "process-fork is not exec",
|
||||
raw: `Sandbox: node(1) deny(1) process-fork`,
|
||||
wantKind: sandbox.ViolationKindGenericDeny,
|
||||
wantOK: false,
|
||||
},
|
||||
{
|
||||
name: "network verb is not recognized",
|
||||
raw: `Sandbox: node(1) deny(1) network-outbound 1.2.3.4:443`,
|
||||
wantKind: sandbox.ViolationKindGenericDeny,
|
||||
wantOK: false,
|
||||
},
|
||||
{
|
||||
name: "log without deny prefix is not recognized",
|
||||
raw: `something unrelated`,
|
||||
wantKind: sandbox.ViolationKindGenericDeny,
|
||||
wantOK: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
kind, label, ok := inferSeatbeltKindFromRawLog(tc.raw)
|
||||
assert.Equal(t, tc.wantOK, ok)
|
||||
assert.Equal(t, tc.wantKind, kind)
|
||||
if tc.wantOK {
|
||||
assert.Equal(t, tc.wantLabel, label)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user