Add comprehensive tests for Go and Python version conflict detection and licensing normalization
- Implemented GoVersionConflictDetectorTests to validate pseudo-version detection, conflict analysis, and conflict retrieval for Go modules. - Created VersionConflictDetectorTests for Python to assess conflict detection across various version scenarios, including major, minor, and patch differences. - Added SpdxLicenseNormalizerTests to ensure accurate normalization of SPDX license strings and classifiers. - Developed VendoredPackageDetectorTests to identify vendored packages and extract embedded packages from Python packages, including handling of vendor directories and known vendored packages.
This commit is contained in:
@@ -101,7 +101,7 @@ public static class ElfDynamicSectionParser
|
||||
if (dynResult.VerneedOffset > 0 && dynResult.VerneedNum > 0 && dynResult.StrtabOffset > 0)
|
||||
{
|
||||
versionNeedsMap = ParseVersionNeeds(span, dynResult.VerneedOffset, dynResult.VerneedNum,
|
||||
dynResult.StrtabOffset, dynResult.StrtabSize, isBigEndian);
|
||||
dynResult.StrtabOffset, dynResult.StrtabSize, is64Bit, isBigEndian);
|
||||
}
|
||||
|
||||
// Build dependencies list with version needs
|
||||
@@ -373,27 +373,134 @@ public static class ElfDynamicSectionParser
|
||||
|
||||
private static Dictionary<string, List<ElfVersionNeed>> ParseVersionNeeds(
|
||||
ReadOnlySpan<byte> span, ulong verneedVaddr, ulong verneedNum,
|
||||
ulong strtabOffset, ulong strtabSize, bool isBigEndian)
|
||||
ulong strtabOffset, ulong strtabSize, bool is64Bit, bool isBigEndian)
|
||||
{
|
||||
var result = new Dictionary<string, List<ElfVersionNeed>>(StringComparer.Ordinal);
|
||||
|
||||
// Find .gnu.version_r section offset (similar to string table lookup)
|
||||
// For now, use a simple heuristic - the section is typically near the string table
|
||||
// In production, we'd properly parse section headers
|
||||
// Find .gnu.version_r section file offset from its virtual address
|
||||
var verneedOffset = FindSectionOffset(span, verneedVaddr, is64Bit, isBigEndian);
|
||||
if (verneedOffset == 0 || verneedOffset >= (ulong)span.Length)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
// The version need structure:
|
||||
// Elf64_Verneed: vn_version (2), vn_cnt (2), vn_file (4), vn_aux (4), vn_next (4)
|
||||
// Elf64_Vernaux: vna_hash (4), vna_flags (2), vna_other (2), vna_name (4), vna_next (4)
|
||||
// Parse Verneed entries
|
||||
// Elf64_Verneed: vn_version (2), vn_cnt (2), vn_file (4), vn_aux (4), vn_next (4) = 16 bytes
|
||||
// Elf32_Verneed: same layout, 16 bytes
|
||||
var currentOffset = (int)verneedOffset;
|
||||
var entriesProcessed = 0uL;
|
||||
|
||||
// For this implementation, we'd need to:
|
||||
// 1. Find the .gnu.version_r section file offset from section headers
|
||||
// 2. Parse each Verneed entry and its aux entries
|
||||
// 3. Map version strings to the file they come from
|
||||
while (entriesProcessed < verneedNum && currentOffset + 16 <= span.Length)
|
||||
{
|
||||
var vnVersion = ReadUInt16(span, currentOffset, isBigEndian);
|
||||
var vnCnt = ReadUInt16(span, currentOffset + 2, isBigEndian);
|
||||
var vnFile = ReadUInt32(span, currentOffset + 4, isBigEndian);
|
||||
var vnAux = ReadUInt32(span, currentOffset + 8, isBigEndian);
|
||||
var vnNext = ReadUInt32(span, currentOffset + 12, isBigEndian);
|
||||
|
||||
// Get the library filename from string table
|
||||
var fileName = ReadNullTerminatedString(span, strtabOffset, strtabSize, vnFile);
|
||||
if (!string.IsNullOrEmpty(fileName))
|
||||
{
|
||||
var versions = new List<ElfVersionNeed>();
|
||||
|
||||
// Parse Vernaux entries for this library
|
||||
// Elf64_Vernaux: vna_hash (4), vna_flags (2), vna_other (2), vna_name (4), vna_next (4) = 16 bytes
|
||||
var auxOffset = currentOffset + (int)vnAux;
|
||||
for (var i = 0; i < vnCnt && auxOffset + 16 <= span.Length; i++)
|
||||
{
|
||||
var vnaHash = ReadUInt32(span, auxOffset, isBigEndian);
|
||||
var vnaFlags = ReadUInt16(span, auxOffset + 4, isBigEndian);
|
||||
var vnaOther = ReadUInt16(span, auxOffset + 6, isBigEndian);
|
||||
var vnaName = ReadUInt32(span, auxOffset + 8, isBigEndian);
|
||||
var vnaNext = ReadUInt32(span, auxOffset + 12, isBigEndian);
|
||||
|
||||
// Get the version string (e.g., "GLIBC_2.17")
|
||||
var versionStr = ReadNullTerminatedString(span, strtabOffset, strtabSize, vnaName);
|
||||
if (!string.IsNullOrEmpty(versionStr))
|
||||
{
|
||||
versions.Add(new ElfVersionNeed(versionStr, vnaHash));
|
||||
}
|
||||
|
||||
if (vnaNext == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
auxOffset += (int)vnaNext;
|
||||
}
|
||||
|
||||
if (versions.Count > 0)
|
||||
{
|
||||
result[fileName] = versions;
|
||||
}
|
||||
}
|
||||
|
||||
entriesProcessed++;
|
||||
if (vnNext == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
currentOffset += (int)vnNext;
|
||||
}
|
||||
|
||||
// This is a simplified placeholder - full implementation would parse section headers
|
||||
return result;
|
||||
}
|
||||
|
||||
private static ulong FindSectionOffset(ReadOnlySpan<byte> span, ulong sectionVaddr, bool is64Bit, bool isBigEndian)
|
||||
{
|
||||
// Parse section headers to find section with matching virtual address
|
||||
ulong shoff;
|
||||
ushort shentsize, shnum;
|
||||
|
||||
if (is64Bit)
|
||||
{
|
||||
shoff = ReadUInt64(span, 40, isBigEndian);
|
||||
shentsize = ReadUInt16(span, 58, isBigEndian);
|
||||
shnum = ReadUInt16(span, 60, isBigEndian);
|
||||
}
|
||||
else
|
||||
{
|
||||
shoff = ReadUInt32(span, 32, isBigEndian);
|
||||
shentsize = ReadUInt16(span, 46, isBigEndian);
|
||||
shnum = ReadUInt16(span, 48, isBigEndian);
|
||||
}
|
||||
|
||||
if (shoff == 0 || shentsize == 0 || shnum == 0)
|
||||
{
|
||||
return sectionVaddr; // Fallback to vaddr as offset
|
||||
}
|
||||
|
||||
for (var i = 0; i < shnum; i++)
|
||||
{
|
||||
var entryOffset = (long)(shoff + (ulong)(i * shentsize));
|
||||
if (entryOffset + shentsize > span.Length)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
var shSpan = span.Slice((int)entryOffset, shentsize);
|
||||
ulong shAddr, shOffset;
|
||||
|
||||
if (is64Bit)
|
||||
{
|
||||
shAddr = ReadUInt64(shSpan, 16, isBigEndian);
|
||||
shOffset = ReadUInt64(shSpan, 24, isBigEndian);
|
||||
}
|
||||
else
|
||||
{
|
||||
shAddr = ReadUInt32(shSpan, 12, isBigEndian);
|
||||
shOffset = ReadUInt32(shSpan, 16, isBigEndian);
|
||||
}
|
||||
|
||||
if (shAddr == sectionVaddr)
|
||||
{
|
||||
return shOffset;
|
||||
}
|
||||
}
|
||||
|
||||
return sectionVaddr; // Fallback to vaddr as offset
|
||||
}
|
||||
|
||||
private static string? ReadNullTerminatedString(ReadOnlySpan<byte> span, ulong strtabOffset, ulong strtabSize, ulong strOffset)
|
||||
{
|
||||
var absoluteOffset = strtabOffset + strOffset;
|
||||
|
||||
@@ -80,7 +80,7 @@ public static class PeImportParser
|
||||
|
||||
if (importRva > 0 && importSize > 0)
|
||||
{
|
||||
dependencies = ParseImportDirectory(span, importRva, sections, "pe-import");
|
||||
dependencies = ParseImportDirectory(span, importRva, sections, "pe-import", is64Bit);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -198,7 +198,7 @@ public static class PeImportParser
|
||||
}
|
||||
|
||||
private static List<PeDeclaredDependency> ParseImportDirectory(
|
||||
ReadOnlySpan<byte> span, uint importRva, List<SectionInfo> sections, string reasonCode)
|
||||
ReadOnlySpan<byte> span, uint importRva, List<SectionInfo> sections, string reasonCode, bool is64Bit)
|
||||
{
|
||||
var dependencies = new List<PeDeclaredDependency>();
|
||||
var seen = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
@@ -231,7 +231,7 @@ public static class PeImportParser
|
||||
if (!string.IsNullOrEmpty(dllName) && seen.Add(dllName))
|
||||
{
|
||||
// Parse imported function names (optional, for detailed analysis)
|
||||
var functions = ParseImportedFunctions(span, originalFirstThunk, sections, is64Bit: false);
|
||||
var functions = ParseImportedFunctions(span, originalFirstThunk, sections, is64Bit);
|
||||
dependencies.Add(new PeDeclaredDependency(dllName, reasonCode, functions));
|
||||
}
|
||||
}
|
||||
@@ -416,7 +416,7 @@ public static class PeImportParser
|
||||
if ((offsetOrData & 0x80000000) != 0)
|
||||
{
|
||||
var subDirOffset = resourceOffset + (int)(offsetOrData & 0x7FFFFFFF);
|
||||
return FindFirstResourceData(span, subDirOffset, resourceOffset);
|
||||
return FindFirstResourceData(span, subDirOffset, resourceOffset, sections);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -426,7 +426,7 @@ public static class PeImportParser
|
||||
return null;
|
||||
}
|
||||
|
||||
private static byte[]? FindFirstResourceData(ReadOnlySpan<byte> span, int dirOffset, int resourceBase)
|
||||
private static byte[]? FindFirstResourceData(ReadOnlySpan<byte> span, int dirOffset, int resourceBase, List<SectionInfo> sections)
|
||||
{
|
||||
if (dirOffset + 16 > span.Length)
|
||||
{
|
||||
@@ -446,31 +446,23 @@ public static class PeImportParser
|
||||
{
|
||||
// Another subdirectory (language level)
|
||||
var langDirOffset = resourceBase + (int)(offsetOrData & 0x7FFFFFFF);
|
||||
return FindFirstResourceData(span, langDirOffset, resourceBase);
|
||||
return FindFirstResourceData(span, langDirOffset, resourceBase, sections);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Data entry
|
||||
// Data entry - IMAGE_RESOURCE_DATA_ENTRY structure
|
||||
var dataEntryOffset = resourceBase + (int)offsetOrData;
|
||||
if (dataEntryOffset + 16 <= span.Length)
|
||||
{
|
||||
var dataRva = BinaryPrimitives.ReadUInt32LittleEndian(span.Slice(dataEntryOffset, 4));
|
||||
var dataSize = BinaryPrimitives.ReadUInt32LittleEndian(span.Slice(dataEntryOffset + 4, 4));
|
||||
|
||||
// For resources, the RVA is relative to the image base, but we need the file offset
|
||||
// Resource data RVA is typically within the .rsrc section
|
||||
var dataOffset = dataEntryOffset - resourceBase + (int)dataRva - (int)dataRva;
|
||||
|
||||
// Actually, we need to convert the RVA properly
|
||||
// Find which section contains this RVA
|
||||
foreach (var section in ParseSectionHeaders(span, 0, 0))
|
||||
// Convert RVA to file offset using section headers
|
||||
var dataOffset = RvaToFileOffset(dataRva, sections);
|
||||
if (dataOffset >= 0 && dataSize > 0 && dataOffset + dataSize <= span.Length)
|
||||
{
|
||||
// This approach won't work without sections, let's use a simpler heuristic
|
||||
return span.Slice(dataOffset, (int)dataSize).ToArray();
|
||||
}
|
||||
|
||||
// Simple heuristic: data is often right after the directory in .rsrc section
|
||||
// For embedded manifests, just search for "<?xml" or "<assembly"
|
||||
return SearchForManifestXml(span);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user