up
This commit is contained in:
@@ -654,6 +654,140 @@ public sealed class EntryTraceAnalyzerTests
|
||||
Assert.Equal(EntryTraceTerminalType.Native, terminal.Type);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ResolveAsync_PropagatesUserSwitchWrapper()
|
||||
{
|
||||
var fs = new TestRootFileSystem();
|
||||
fs.AddBinaryFile("/usr/bin/sudo", CreateGoBinary(), executable: true);
|
||||
fs.AddBinaryFile("/usr/bin/python", CreateGoBinary(), executable: true);
|
||||
fs.AddFile("/srv/app.py", "print('hi')\n", executable: false);
|
||||
|
||||
var context = new EntryTraceContext(
|
||||
fs,
|
||||
ImmutableDictionary<string, string>.Empty,
|
||||
ImmutableArray.Create("/usr/bin"),
|
||||
"/",
|
||||
"root",
|
||||
"sha256:user-switch",
|
||||
"scan-sudo",
|
||||
NullLogger.Instance);
|
||||
|
||||
var spec = EntrypointSpecification.FromExecForm(
|
||||
new[] { "sudo", "-u", "app", "python", "/srv/app.py" },
|
||||
null);
|
||||
|
||||
var analyzer = CreateAnalyzer();
|
||||
var result = await analyzer.ResolveAsync(spec, context);
|
||||
|
||||
var terminal = Assert.Single(result.Terminals);
|
||||
Assert.Equal("/usr/bin/python", terminal.Path);
|
||||
Assert.Equal("app", terminal.User);
|
||||
Assert.Contains("/srv/app.py", terminal.Arguments);
|
||||
|
||||
var edge = Assert.Single(result.Edges.Where(e => e.Relationship == "wrapper"));
|
||||
Assert.Equal("true", edge.Metadata?["guarded"]);
|
||||
Assert.Equal("user", edge.Metadata?["state-change"]);
|
||||
Assert.Equal("app", edge.Metadata?["user"]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ResolveAsync_PropagatesEnvWrapperIntoPlan()
|
||||
{
|
||||
var fs = new TestRootFileSystem();
|
||||
fs.AddBinaryFile("/usr/bin/env", CreateGoBinary(), executable: true);
|
||||
fs.AddBinaryFile("/usr/bin/python", CreateGoBinary(), executable: true);
|
||||
fs.AddFile("/srv/app.py", "print('env')\n", executable: false);
|
||||
|
||||
var context = new EntryTraceContext(
|
||||
fs,
|
||||
ImmutableDictionary<string, string>.Empty,
|
||||
ImmutableArray.Create("/usr/bin"),
|
||||
"/",
|
||||
"root",
|
||||
"sha256:env-wrapper",
|
||||
"scan-env",
|
||||
NullLogger.Instance);
|
||||
|
||||
var spec = EntrypointSpecification.FromExecForm(
|
||||
new[] { "env", "FOO=bar", "python", "/srv/app.py" },
|
||||
null);
|
||||
|
||||
var analyzer = CreateAnalyzer();
|
||||
var result = await analyzer.ResolveAsync(spec, context);
|
||||
|
||||
var plan = Assert.Single(result.Plans);
|
||||
Assert.True(plan.Environment.TryGetValue("FOO", out var value) && value == "bar");
|
||||
var terminal = Assert.Single(result.Terminals);
|
||||
Assert.Equal("/usr/bin/python", terminal.Path);
|
||||
|
||||
var edge = Assert.Single(result.Edges.Where(e => e.Relationship == "wrapper"));
|
||||
Assert.Equal("env", edge.Metadata?["state-change"]);
|
||||
Assert.Equal("true", edge.Metadata?["guarded"]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ResolveAsync_AccumulatesWorkingDirectoryFromShellCd()
|
||||
{
|
||||
var fs = new TestRootFileSystem();
|
||||
fs.AddBinaryFile("/bin/sh", CreateGoBinary(), executable: true);
|
||||
fs.AddBinaryFile("/usr/bin/python", CreateGoBinary(), executable: true);
|
||||
fs.AddFile("/entry.sh", "#!/bin/sh\ncd /service\nexec python /srv/service.py\n", executable: true);
|
||||
fs.AddFile("/srv/service.py", "print('svc')\n", executable: false);
|
||||
|
||||
var context = new EntryTraceContext(
|
||||
fs,
|
||||
ImmutableDictionary<string, string>.Empty,
|
||||
ImmutableArray.Create("/bin", "/usr/bin"),
|
||||
"/",
|
||||
"root",
|
||||
"sha256:cd-trace",
|
||||
"scan-cd",
|
||||
NullLogger.Instance);
|
||||
|
||||
var spec = EntrypointSpecification.FromExecForm(
|
||||
new[] { "/entry.sh" },
|
||||
null);
|
||||
|
||||
var analyzer = CreateAnalyzer();
|
||||
var result = await analyzer.ResolveAsync(spec, context);
|
||||
|
||||
var terminal = Assert.Single(result.Terminals);
|
||||
Assert.Equal("/usr/bin/python", terminal.Path);
|
||||
Assert.Equal("/service", terminal.WorkingDirectory);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ResolveAsync_HandlesInitShimAndGuardsEdge()
|
||||
{
|
||||
var fs = new TestRootFileSystem();
|
||||
fs.AddBinaryFile("/sbin/tini", CreateGoBinary(), executable: true);
|
||||
fs.AddBinaryFile("/usr/bin/python", CreateGoBinary(), executable: true);
|
||||
fs.AddFile("/srv/app.py", "print('shim')\n", executable: false);
|
||||
|
||||
var context = new EntryTraceContext(
|
||||
fs,
|
||||
ImmutableDictionary<string, string>.Empty,
|
||||
ImmutableArray.Create("/sbin", "/usr/bin"),
|
||||
"/",
|
||||
"root",
|
||||
"sha256:init-shim",
|
||||
"scan-tini",
|
||||
NullLogger.Instance);
|
||||
|
||||
var spec = EntrypointSpecification.FromExecForm(
|
||||
new[] { "/sbin/tini", "--", "python", "/srv/app.py" },
|
||||
null);
|
||||
|
||||
var analyzer = CreateAnalyzer();
|
||||
var result = await analyzer.ResolveAsync(spec, context);
|
||||
|
||||
var terminal = Assert.Single(result.Terminals);
|
||||
Assert.Equal("/usr/bin/python", terminal.Path);
|
||||
var edge = Assert.Single(result.Edges.Where(e => e.Relationship == "wrapper"));
|
||||
Assert.Equal("true", edge.Metadata?["guarded"]);
|
||||
Assert.Equal("init", edge.Metadata?["shim"]);
|
||||
}
|
||||
|
||||
private static byte[] CreateGoBinary()
|
||||
{
|
||||
var buffer = new byte[256];
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using StellaOps.Scanner.EntryTrace;
|
||||
@@ -29,6 +30,26 @@ public sealed class EntryTraceRuntimeReconcilerTests
|
||||
Assert.Contains(reconciled.Diagnostics, d => d.Reason == EntryTraceUnknownReason.RuntimeMatch);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Reconcile_EmitsRuntimeChain_InDiagnostics()
|
||||
{
|
||||
var reconciler = new EntryTraceRuntimeReconciler();
|
||||
var graph = CreateGraph("/usr/local/bin/app");
|
||||
|
||||
var procGraph = ProcGraphBuilder.Build(new FakeProvider(new[]
|
||||
{
|
||||
CreateProcess(1, 0, "/sbin/tini", "tini", 100),
|
||||
CreateProcess(5, 1, "/usr/local/bin/app", "app", 200),
|
||||
}));
|
||||
|
||||
var reconciled = reconciler.Reconcile(graph, procGraph);
|
||||
|
||||
var diag = Assert.Single(reconciled.Diagnostics, d => d.Reason == EntryTraceUnknownReason.RuntimeMatch);
|
||||
Assert.Contains("tini", diag.Message, StringComparison.OrdinalIgnoreCase);
|
||||
Assert.Contains("/usr/local/bin/app", diag.Message, StringComparison.OrdinalIgnoreCase);
|
||||
Assert.Contains("->", diag.Message, StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Reconcile_FlagsMismatch_WhenDifferentExecutable()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user