167 lines
5.7 KiB
C#
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();
|
|
}
|
|
}
|