feat: Add native binary analyzer test utilities and implement SM2 signing tests
Some checks failed
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Docs CI / lint-and-preview (push) Has been cancelled
Manifest Integrity / Audit SHA256SUMS Files (push) Has been cancelled
Manifest Integrity / Validate Schema Integrity (push) Has been cancelled
Manifest Integrity / Validate Contract Documents (push) Has been cancelled
Manifest Integrity / Validate Pack Fixtures (push) Has been cancelled
Manifest Integrity / Verify Merkle Roots (push) Has been cancelled
Scanner Analyzers / Build Analyzers (push) Has been cancelled
Scanner Analyzers / Discover Analyzers (push) Has been cancelled
Scanner Analyzers / Test Language Analyzers (push) Has been cancelled
Scanner Analyzers / Validate Test Fixtures (push) Has been cancelled
Scanner Analyzers / Verify Deterministic Output (push) Has been cancelled
Signals CI & Image / signals-ci (push) Has been cancelled
Concelier Attestation Tests / attestation-tests (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled
Export Center CI / export-ci (push) Has been cancelled
Notify Smoke Test / Notify Unit Tests (push) Has been cancelled
Notify Smoke Test / Notifier Service Tests (push) Has been cancelled
Notify Smoke Test / Notification Smoke Test (push) Has been cancelled
Some checks failed
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Docs CI / lint-and-preview (push) Has been cancelled
Manifest Integrity / Audit SHA256SUMS Files (push) Has been cancelled
Manifest Integrity / Validate Schema Integrity (push) Has been cancelled
Manifest Integrity / Validate Contract Documents (push) Has been cancelled
Manifest Integrity / Validate Pack Fixtures (push) Has been cancelled
Manifest Integrity / Verify Merkle Roots (push) Has been cancelled
Scanner Analyzers / Build Analyzers (push) Has been cancelled
Scanner Analyzers / Discover Analyzers (push) Has been cancelled
Scanner Analyzers / Test Language Analyzers (push) Has been cancelled
Scanner Analyzers / Validate Test Fixtures (push) Has been cancelled
Scanner Analyzers / Verify Deterministic Output (push) Has been cancelled
Signals CI & Image / signals-ci (push) Has been cancelled
Concelier Attestation Tests / attestation-tests (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled
Export Center CI / export-ci (push) Has been cancelled
Notify Smoke Test / Notify Unit Tests (push) Has been cancelled
Notify Smoke Test / Notifier Service Tests (push) Has been cancelled
Notify Smoke Test / Notification Smoke Test (push) Has been cancelled
- Introduced `NativeTestBase` class for ELF, PE, and Mach-O binary parsing helpers and assertions. - Created `TestCryptoFactory` for SM2 cryptographic provider setup and key generation. - Implemented `Sm2SigningTests` to validate signing functionality with environment gate checks. - Developed console export service and store with comprehensive unit tests for export status management.
This commit is contained in:
@@ -0,0 +1,203 @@
|
||||
using StellaOps.Scanner.Analyzers.Lang.Php.Internal;
|
||||
|
||||
namespace StellaOps.Scanner.Analyzers.Lang.Php.Tests.Internal;
|
||||
|
||||
public sealed class PhpFfiDetectorTests
|
||||
{
|
||||
[Fact]
|
||||
public void AnalyzeFileContent_DetectsFfiCdef()
|
||||
{
|
||||
var content = @"
|
||||
<?php
|
||||
|
||||
$ffi = FFI::cdef('
|
||||
int printf(const char *format, ...);
|
||||
void exit(int status);
|
||||
', 'libc.so.6');
|
||||
|
||||
$ffi->printf('Hello, World!\n');
|
||||
";
|
||||
|
||||
var result = PhpFfiDetector.AnalyzeFileContent(content, "test.php");
|
||||
|
||||
Assert.NotEmpty(result);
|
||||
Assert.Contains(result, u => u.Kind == FfiUsageKind.Cdef);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AnalyzeFileContent_DetectsFfiLoad()
|
||||
{
|
||||
var content = @"
|
||||
<?php
|
||||
|
||||
// Load FFI from header file
|
||||
$ffi = FFI::load('mylib.h');
|
||||
$ffi->myFunction();
|
||||
";
|
||||
|
||||
var result = PhpFfiDetector.AnalyzeFileContent(content, "test.php");
|
||||
|
||||
Assert.NotEmpty(result);
|
||||
Assert.Contains(result, u => u.Kind == FfiUsageKind.Load);
|
||||
Assert.Contains(result, u => u.LibraryName == "mylib.h");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AnalyzeFileContent_DetectsFfiNew()
|
||||
{
|
||||
var content = @"
|
||||
<?php
|
||||
|
||||
$ffi = FFI::cdef('struct Point { int x; int y; };');
|
||||
$point = FFI::new('struct Point');
|
||||
$point->x = 10;
|
||||
$point->y = 20;
|
||||
";
|
||||
|
||||
var result = PhpFfiDetector.AnalyzeFileContent(content, "test.php");
|
||||
|
||||
Assert.NotEmpty(result);
|
||||
Assert.Contains(result, u => u.Kind == FfiUsageKind.New);
|
||||
Assert.Contains(result, u => u.Definition == "struct Point");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AnalyzeFileContent_DetectsFfiType()
|
||||
{
|
||||
var content = @"
|
||||
<?php
|
||||
|
||||
$type = FFI::type('uint32_t');
|
||||
$arr = FFI::new($type, 10);
|
||||
";
|
||||
|
||||
var result = PhpFfiDetector.AnalyzeFileContent(content, "test.php");
|
||||
|
||||
Assert.NotEmpty(result);
|
||||
Assert.Contains(result, u => u.Kind == FfiUsageKind.Type);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AnalyzeFileContent_DetectsFfiCast()
|
||||
{
|
||||
var content = @"
|
||||
<?php
|
||||
|
||||
$ptr = FFI::new('void*');
|
||||
$intPtr = FFI::cast('int*', $ptr);
|
||||
";
|
||||
|
||||
var result = PhpFfiDetector.AnalyzeFileContent(content, "test.php");
|
||||
|
||||
Assert.NotEmpty(result);
|
||||
Assert.Contains(result, u => u.Kind == FfiUsageKind.Cast);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AnalyzeFileContent_DetectsFfiScope()
|
||||
{
|
||||
var content = @"
|
||||
<?php
|
||||
|
||||
// Using preloaded FFI
|
||||
$ffi = FFI::scope('mylib');
|
||||
$result = $ffi->calculate(42);
|
||||
";
|
||||
|
||||
var result = PhpFfiDetector.AnalyzeFileContent(content, "test.php");
|
||||
|
||||
Assert.NotEmpty(result);
|
||||
Assert.Contains(result, u => u.Kind == FfiUsageKind.Scope);
|
||||
Assert.Contains(result, u => u.Definition == "mylib");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AnalyzeFileContent_NoFfiUsage_ReturnsEmpty()
|
||||
{
|
||||
var content = @"
|
||||
<?php
|
||||
|
||||
function calculate($x, $y) {
|
||||
return $x + $y;
|
||||
}
|
||||
|
||||
echo calculate(10, 20);
|
||||
";
|
||||
|
||||
var result = PhpFfiDetector.AnalyzeFileContent(content, "test.php");
|
||||
|
||||
Assert.Empty(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AnalyzeFileContent_MultipleFfiUsages_ReturnsAll()
|
||||
{
|
||||
var content = @"
|
||||
<?php
|
||||
|
||||
$ffi = FFI::cdef('int abs(int x);', 'libc.so.6');
|
||||
$ffi2 = FFI::load('custom.h');
|
||||
$val = FFI::new('int');
|
||||
";
|
||||
|
||||
var result = PhpFfiDetector.AnalyzeFileContent(content, "test.php");
|
||||
|
||||
Assert.Equal(3, result.Count);
|
||||
Assert.Contains(result, u => u.Kind == FfiUsageKind.Cdef);
|
||||
Assert.Contains(result, u => u.Kind == FfiUsageKind.Load);
|
||||
Assert.Contains(result, u => u.Kind == FfiUsageKind.New);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AnalyzeFileContent_CaseInsensitive_DetectsFfi()
|
||||
{
|
||||
var content = @"
|
||||
<?php
|
||||
|
||||
// Various case combinations
|
||||
$a = ffi::cdef('void foo();');
|
||||
$b = Ffi::Load('lib.h');
|
||||
$c = FFi::NEW('int');
|
||||
";
|
||||
|
||||
var result = PhpFfiDetector.AnalyzeFileContent(content, "test.php");
|
||||
|
||||
Assert.Equal(3, result.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FfiAnalysisResult_CreateMetadata_IncludesAllFields()
|
||||
{
|
||||
var result = new FfiAnalysisResult(
|
||||
FfiEnabledSetting.On,
|
||||
["file1.php", "file2.php"],
|
||||
[
|
||||
new FfiUsage(FfiUsageKind.Cdef, "file1.php", 10, "FFI::cdef(...)", null, "int foo();"),
|
||||
new FfiUsage(FfiUsageKind.Load, "file2.php", 5, "FFI::load('lib.so')", "lib.so", null)
|
||||
],
|
||||
["lib.so"],
|
||||
[new FfiDefinition("file1.php", "int foo();")],
|
||||
["vendor/lib/native.so"]);
|
||||
|
||||
var metadata = result.CreateMetadata().ToDictionary(kv => kv.Key, kv => kv.Value);
|
||||
|
||||
Assert.Equal("true", metadata["ffi.detected"]);
|
||||
Assert.Equal("on", metadata["ffi.enabled_setting"]);
|
||||
Assert.Equal("2", metadata["ffi.usage_count"]);
|
||||
Assert.Contains("file1.php", metadata["ffi.files_with_usage"]);
|
||||
Assert.Contains("lib.so", metadata["ffi.libraries"]);
|
||||
Assert.Equal("1", metadata["ffi.definition_count"]);
|
||||
Assert.Equal("1", metadata["ffi.native_library_count"]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FfiAnalysisResult_Empty_HasNoFfiUsage()
|
||||
{
|
||||
var result = FfiAnalysisResult.Empty;
|
||||
|
||||
Assert.False(result.HasFfiUsage);
|
||||
Assert.Empty(result.Usages);
|
||||
Assert.Empty(result.FilesWithFfi);
|
||||
Assert.Empty(result.Libraries);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,253 @@
|
||||
using StellaOps.Scanner.Analyzers.Lang.Php.Internal;
|
||||
|
||||
namespace StellaOps.Scanner.Analyzers.Lang.Php.Tests.Internal;
|
||||
|
||||
public sealed class PhpVersionConflictDetectorTests
|
||||
{
|
||||
[Fact]
|
||||
public void Analyze_NullInputs_ReturnsEmpty()
|
||||
{
|
||||
var result = PhpVersionConflictDetector.Analyze(null, null);
|
||||
|
||||
Assert.False(result.HasConflicts);
|
||||
Assert.Empty(result.Conflicts);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Analyze_EmptyLockData_ReturnsEmpty()
|
||||
{
|
||||
var manifest = CreateManifest(new Dictionary<string, string>
|
||||
{
|
||||
["symfony/console"] = "^6.0"
|
||||
});
|
||||
|
||||
var result = PhpVersionConflictDetector.Analyze(manifest, ComposerLockData.Empty);
|
||||
|
||||
Assert.False(result.HasConflicts);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Analyze_OldPhpVersion_DetectsConflict()
|
||||
{
|
||||
var manifest = CreateManifest(new Dictionary<string, string>
|
||||
{
|
||||
["php"] = "^5.6"
|
||||
});
|
||||
var lockData = CreateLockData([]);
|
||||
|
||||
var result = PhpVersionConflictDetector.Analyze(manifest, lockData);
|
||||
|
||||
Assert.True(result.HasConflicts);
|
||||
Assert.Contains(result.Conflicts, c => c.PackageName == "php" && c.ConflictType == PhpConflictType.PlatformRequirement);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Analyze_DeprecatedExtension_DetectsConflict()
|
||||
{
|
||||
var manifest = CreateManifest(new Dictionary<string, string>
|
||||
{
|
||||
["ext-mcrypt"] = "*"
|
||||
});
|
||||
var lockData = CreateLockData([]);
|
||||
|
||||
var result = PhpVersionConflictDetector.Analyze(manifest, lockData);
|
||||
|
||||
Assert.True(result.HasConflicts);
|
||||
Assert.Contains(result.Conflicts, c =>
|
||||
c.PackageName == "ext-mcrypt" &&
|
||||
c.ConflictType == PhpConflictType.DeprecatedExtension &&
|
||||
c.Severity == PhpConflictSeverity.High);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Analyze_MissingPackage_DetectsConflict()
|
||||
{
|
||||
var manifest = CreateManifest(new Dictionary<string, string>
|
||||
{
|
||||
["vendor/missing-package"] = "^1.0"
|
||||
});
|
||||
var lockData = CreateLockData([]);
|
||||
|
||||
var result = PhpVersionConflictDetector.Analyze(manifest, lockData);
|
||||
|
||||
Assert.True(result.HasConflicts);
|
||||
Assert.Contains(result.Conflicts, c =>
|
||||
c.PackageName == "vendor/missing-package" &&
|
||||
c.ConflictType == PhpConflictType.MissingPackage);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Analyze_DevVersionWithStableConstraint_DetectsConflict()
|
||||
{
|
||||
var manifest = CreateManifest(new Dictionary<string, string>
|
||||
{
|
||||
["vendor/package"] = "^1.0"
|
||||
});
|
||||
var lockData = CreateLockData([
|
||||
new ComposerPackage("vendor/package", "dev-main", "library", false, null, null, null, null, ComposerAutoloadData.Empty)
|
||||
]);
|
||||
|
||||
var result = PhpVersionConflictDetector.Analyze(manifest, lockData);
|
||||
|
||||
Assert.True(result.HasConflicts);
|
||||
Assert.Contains(result.Conflicts, c =>
|
||||
c.PackageName == "vendor/package" &&
|
||||
c.ConflictType == PhpConflictType.UnstableVersion);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Analyze_ZeroVersion_DetectsUnstableApi()
|
||||
{
|
||||
var manifest = CreateManifest(new Dictionary<string, string>
|
||||
{
|
||||
["vendor/package"] = "^0.1"
|
||||
});
|
||||
var lockData = CreateLockData([
|
||||
new ComposerPackage("vendor/package", "0.1.5", "library", false, null, null, null, null, ComposerAutoloadData.Empty)
|
||||
]);
|
||||
|
||||
var result = PhpVersionConflictDetector.Analyze(manifest, lockData);
|
||||
|
||||
Assert.True(result.HasConflicts);
|
||||
Assert.Contains(result.Conflicts, c =>
|
||||
c.PackageName == "vendor/package" &&
|
||||
c.ConflictType == PhpConflictType.UnstableApi);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Analyze_AbandonedPackage_DetectsConflict()
|
||||
{
|
||||
var manifest = CreateManifest(new Dictionary<string, string>
|
||||
{
|
||||
["swiftmailer/swiftmailer"] = "^6.0"
|
||||
});
|
||||
var lockData = CreateLockData([
|
||||
new ComposerPackage("swiftmailer/swiftmailer", "6.3.0", "library", false, null, null, null, null, ComposerAutoloadData.Empty)
|
||||
]);
|
||||
|
||||
var result = PhpVersionConflictDetector.Analyze(manifest, lockData);
|
||||
|
||||
Assert.True(result.HasConflicts);
|
||||
Assert.Contains(result.Conflicts, c =>
|
||||
c.PackageName == "swiftmailer/swiftmailer" &&
|
||||
c.ConflictType == PhpConflictType.AbandonedPackage &&
|
||||
c.Message.Contains("symfony/mailer"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Analyze_NoConflicts_ReturnsEmpty()
|
||||
{
|
||||
var manifest = CreateManifest(new Dictionary<string, string>
|
||||
{
|
||||
["php"] = "^8.0",
|
||||
["symfony/console"] = "^6.0"
|
||||
});
|
||||
var lockData = CreateLockData([
|
||||
new ComposerPackage("symfony/console", "6.4.0", "library", false, null, null, null, null, ComposerAutoloadData.Empty)
|
||||
]);
|
||||
|
||||
var result = PhpVersionConflictDetector.Analyze(manifest, lockData);
|
||||
|
||||
// No conflicts for stable packages with matching constraints
|
||||
var nonLowConflicts = result.Conflicts.Where(c => c.Severity != PhpConflictSeverity.Low);
|
||||
Assert.Empty(nonLowConflicts);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetConflict_ReturnsConflictForPackage()
|
||||
{
|
||||
var manifest = CreateManifest(new Dictionary<string, string>
|
||||
{
|
||||
["ext-mysql"] = "*"
|
||||
});
|
||||
var lockData = CreateLockData([]);
|
||||
|
||||
var result = PhpVersionConflictDetector.Analyze(manifest, lockData);
|
||||
var conflict = result.GetConflict("ext-mysql");
|
||||
|
||||
Assert.NotNull(conflict);
|
||||
Assert.Equal("ext-mysql", conflict.PackageName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetBySeverity_FiltersCorrectly()
|
||||
{
|
||||
var manifest = CreateManifest(new Dictionary<string, string>
|
||||
{
|
||||
["ext-mcrypt"] = "*", // High severity
|
||||
["php"] = "^5.6" // Medium severity
|
||||
});
|
||||
var lockData = CreateLockData([]);
|
||||
|
||||
var result = PhpVersionConflictDetector.Analyze(manifest, lockData);
|
||||
|
||||
var high = result.GetBySeverity(PhpConflictSeverity.High).ToList();
|
||||
var medium = result.GetBySeverity(PhpConflictSeverity.Medium).ToList();
|
||||
|
||||
Assert.NotEmpty(high);
|
||||
Assert.NotEmpty(medium);
|
||||
Assert.All(high, c => Assert.Equal(PhpConflictSeverity.High, c.Severity));
|
||||
Assert.All(medium, c => Assert.Equal(PhpConflictSeverity.Medium, c.Severity));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PhpConflictAnalysis_CreateMetadata_IncludesAllFields()
|
||||
{
|
||||
var manifest = CreateManifest(new Dictionary<string, string>
|
||||
{
|
||||
["ext-mcrypt"] = "*",
|
||||
["php"] = "^5.6"
|
||||
});
|
||||
var lockData = CreateLockData([]);
|
||||
|
||||
var result = PhpVersionConflictDetector.Analyze(manifest, lockData);
|
||||
var metadata = result.CreateMetadata().ToDictionary(kv => kv.Key, kv => kv.Value);
|
||||
|
||||
Assert.Equal("true", metadata["conflict.detected"]);
|
||||
Assert.True(int.Parse(metadata["conflict.count"]!) > 0);
|
||||
Assert.NotNull(metadata["conflict.severity"]);
|
||||
Assert.Contains("conflict.types", metadata.Keys);
|
||||
Assert.Contains("conflict.packages", metadata.Keys);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PhpConflictAnalysis_Empty_HasNoConflicts()
|
||||
{
|
||||
var result = PhpConflictAnalysis.Empty;
|
||||
|
||||
Assert.False(result.HasConflicts);
|
||||
Assert.Empty(result.Conflicts);
|
||||
Assert.Null(result.HighestSeverity);
|
||||
}
|
||||
|
||||
private static PhpComposerManifest CreateManifest(IReadOnlyDictionary<string, string> require)
|
||||
{
|
||||
return new PhpComposerManifest(
|
||||
manifestPath: "composer.json",
|
||||
name: "test/project",
|
||||
description: null,
|
||||
type: "project",
|
||||
version: null,
|
||||
license: "MIT",
|
||||
authors: [],
|
||||
require: require,
|
||||
requireDev: new Dictionary<string, string>(),
|
||||
autoload: ComposerAutoloadData.Empty,
|
||||
autoloadDev: ComposerAutoloadData.Empty,
|
||||
scripts: new Dictionary<string, string>(),
|
||||
bin: new Dictionary<string, string>(),
|
||||
minimumStability: null,
|
||||
sha256: null);
|
||||
}
|
||||
|
||||
private static ComposerLockData CreateLockData(IReadOnlyList<ComposerPackage> packages)
|
||||
{
|
||||
return new ComposerLockData(
|
||||
lockPath: "composer.lock",
|
||||
contentHash: null,
|
||||
pluginApiVersion: null,
|
||||
packages: packages,
|
||||
devPackages: [],
|
||||
lockSha256: null);
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@
|
||||
<LangVersion>preview</LangVersion>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user