Files
git.stella-ops.org/src/Scanner/__Tests/StellaOps.Scanner.Analyzers.Native.Tests/ElfDynamicSectionParserTests.cs

167 lines
5.7 KiB
C#

using FluentAssertions;
using StellaOps.Scanner.Analyzers.Native;
using StellaOps.Scanner.Analyzers.Native.Tests.Fixtures;
using StellaOps.Scanner.Analyzers.Native.Tests.TestUtilities;
using StellaOps.TestKit;
namespace StellaOps.Scanner.Analyzers.Native.Tests;
public class ElfDynamicSectionParserTests : NativeTestBase
{
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ParsesMinimalElfWithNoDynamicSection()
{
// Minimal ELF64 with no dependencies (static binary scenario)
var elf = ElfBuilder.Static().Build();
var result = TryParseElf(elf, out var info);
result.Should().BeTrue();
info.Dependencies.Should().BeEmpty();
info.Rpath.Should().BeEmpty();
info.Runpath.Should().BeEmpty();
}
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ParsesElfWithDtNeeded()
{
// Build ELF with DT_NEEDED entries using the builder
var elf = ElfBuilder.LinuxX64()
.AddDependencies("libc.so.6", "libm.so.6", "libpthread.so.0")
.Build();
var info = ParseElf(elf);
info.Dependencies.Should().HaveCount(3);
info.Dependencies[0].Soname.Should().Be("libc.so.6");
info.Dependencies[0].ReasonCode.Should().Be("elf-dtneeded");
info.Dependencies[1].Soname.Should().Be("libm.so.6");
info.Dependencies[2].Soname.Should().Be("libpthread.so.0");
}
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ParsesElfWithRpathAndRunpath()
{
// Build ELF with rpath and runpath using the builder
var elf = ElfBuilder.LinuxX64()
.WithRpath("/opt/lib", "/usr/local/lib")
.WithRunpath("$ORIGIN/../lib")
.Build();
var info = ParseElf(elf);
info.Rpath.Should().BeEquivalentTo(["/opt/lib", "/usr/local/lib"]);
info.Runpath.Should().BeEquivalentTo(["$ORIGIN/../lib"]);
}
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ParsesElfWithInterpreterAndBuildId()
{
// Build ELF with interpreter and build ID using the builder
var elf = ElfBuilder.LinuxX64()
.WithBuildId("deadbeef0102030405060708090a0b0c")
.Build();
var info = ParseElf(elf);
info.Interpreter.Should().Be("/lib64/ld-linux-x86-64.so.2");
info.BinaryId.Should().Be("deadbeef0102030405060708090a0b0c");
}
[Trait("Category", TestCategories.Unit)]
[Fact]
public void DeduplicatesDtNeededEntries()
{
// ElfBuilder deduplicates internally, so add "duplicates" via builder
// The builder will produce correct output, and we verify the parser handles it
var elf = ElfBuilder.LinuxX64()
.AddDependency("libc.so.6")
.AddDependency("libc.so.6") // Duplicate - builder should handle this
.AddDependency("libc.so.6") // Triple duplicate
.Build();
var info = ParseElf(elf);
// Whether builder deduplicates or not, parser should return unique deps
info.Dependencies.Should().HaveCount(1);
info.Dependencies[0].Soname.Should().Be("libc.so.6");
}
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ReturnsFalseForNonElfData()
{
var buffer = new byte[] { 0x00, 0x01, 0x02, 0x03 };
using var stream = new MemoryStream(buffer);
var result = ElfDynamicSectionParser.TryParse(stream, out var info);
result.Should().BeFalse();
}
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ReturnsFalseForPeFile()
{
var buffer = new byte[256];
buffer[0] = (byte)'M';
buffer[1] = (byte)'Z';
using var stream = new MemoryStream(buffer);
var result = ElfDynamicSectionParser.TryParse(stream, out var info);
result.Should().BeFalse();
}
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ParsesElfWithVersionNeeds()
{
// Test that version needs (GLIBC_2.17, etc.) are properly extracted
var elf = ElfBuilder.LinuxX64()
.AddDependency("libc.so.6")
.AddVersionNeed("libc.so.6", "GLIBC_2.17", isWeak: false)
.AddVersionNeed("libc.so.6", "GLIBC_2.28", isWeak: false)
.Build();
var info = ParseElf(elf);
info.Dependencies.Should().HaveCount(1);
info.Dependencies[0].Soname.Should().Be("libc.so.6");
info.Dependencies[0].VersionNeeds.Should().HaveCount(2);
info.Dependencies[0].VersionNeeds.Should().Contain(v => v.Version == "GLIBC_2.17");
info.Dependencies[0].VersionNeeds.Should().Contain(v => v.Version == "GLIBC_2.28");
}
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ParsesElfWithWeakVersionNeeds()
{
// Test that weak version requirements (VER_FLG_WEAK) are properly detected
var elf = ElfBuilder.LinuxX64()
.AddDependency("libc.so.6")
.AddVersionNeed("libc.so.6", "GLIBC_2.17", isWeak: false) // Required version
.AddVersionNeed("libc.so.6", "GLIBC_2.34", isWeak: true) // Weak/optional version
.Build();
var info = ParseElf(elf);
info.Dependencies.Should().HaveCount(1);
info.Dependencies[0].Soname.Should().Be("libc.so.6");
info.Dependencies[0].VersionNeeds.Should().HaveCount(2);
// GLIBC_2.17 should NOT be weak
var glibc217 = info.Dependencies[0].VersionNeeds.First(v => v.Version == "GLIBC_2.17");
glibc217.IsWeak.Should().BeFalse();
// GLIBC_2.34 should BE weak
var glibc234 = info.Dependencies[0].VersionNeeds.First(v => v.Version == "GLIBC_2.34");
glibc234.IsWeak.Should().BeTrue();
}
}