up
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
Findings Ledger CI / build-test (push) Has been cancelled
Findings Ledger CI / migration-validation (push) Has been cancelled
Scanner Analyzers / Discover Analyzers (push) Has been cancelled
Signals Reachability Scoring & Events / reachability-smoke (push) Has been cancelled
AOC Guard CI / aoc-guard (push) Has been cancelled
Concelier Attestation Tests / attestation-tests (push) Has been cancelled
cryptopro-linux-csp / build-and-test (push) Has been cancelled
Scanner Analyzers / Validate Test Fixtures (push) Has been cancelled
Signals CI & Image / signals-ci (push) Has been cancelled
sm-remote-ci / build-and-test (push) Has been cancelled
Findings Ledger CI / generate-manifest (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Scanner Analyzers / Build Analyzers (push) Has been cancelled
Scanner Analyzers / Test Language Analyzers (push) Has been cancelled
Scanner Analyzers / Verify Deterministic Output (push) Has been cancelled
Signals Reachability Scoring & Events / sign-and-upload (push) Has been cancelled
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
Findings Ledger CI / build-test (push) Has been cancelled
Findings Ledger CI / migration-validation (push) Has been cancelled
Scanner Analyzers / Discover Analyzers (push) Has been cancelled
Signals Reachability Scoring & Events / reachability-smoke (push) Has been cancelled
AOC Guard CI / aoc-guard (push) Has been cancelled
Concelier Attestation Tests / attestation-tests (push) Has been cancelled
cryptopro-linux-csp / build-and-test (push) Has been cancelled
Scanner Analyzers / Validate Test Fixtures (push) Has been cancelled
Signals CI & Image / signals-ci (push) Has been cancelled
sm-remote-ci / build-and-test (push) Has been cancelled
Findings Ledger CI / generate-manifest (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Scanner Analyzers / Build Analyzers (push) Has been cancelled
Scanner Analyzers / Test Language Analyzers (push) Has been cancelled
Scanner Analyzers / Verify Deterministic Output (push) Has been cancelled
Signals Reachability Scoring & Events / sign-and-upload (push) Has been cancelled
This commit is contained in:
@@ -21,7 +21,7 @@
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\\__Libraries\\StellaOps.Scanner.Surface.FS\\StellaOps.Scanner.Surface.FS.csproj" />
|
||||
<ProjectReference Include="..\\__Libraries\\StellaOps.Scanner.Surface.Secrets\\StellaOps.Scanner.Surface.Secrets.csproj" />
|
||||
<PackageReference Include="StellaOps.Scanner.Surface.Env" Version="0.1.0-alpha.20251123" />
|
||||
<ProjectReference Include="..\\__Libraries\\StellaOps.Scanner.Surface.Env\\StellaOps.Scanner.Surface.Env.csproj" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="9.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="9.0.0" />
|
||||
|
||||
@@ -133,8 +133,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Concelier.Testing
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Concelier.Connector.Common", "..\Concelier\__Libraries\StellaOps.Concelier.Connector.Common\StellaOps.Concelier.Connector.Common.csproj", "{09F93E81-05B5-46CB-818D-BDD2812CCF71}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Concelier.Storage.Mongo", "..\Concelier\__Libraries\StellaOps.Concelier.Storage.Mongo\StellaOps.Concelier.Storage.Mongo.csproj", "{87E9CDA0-F6EB-4D7F-85E1-0C9288E2717C}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Concelier.Core", "..\Concelier\__Libraries\StellaOps.Concelier.Core\StellaOps.Concelier.Core.csproj", "{9CBE8002-B289-4A86-91C9-5CD405149B2A}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Concelier.Models", "..\Concelier\__Libraries\StellaOps.Concelier.Models\StellaOps.Concelier.Models.csproj", "{9A16F25A-99B9-4082-85AD-C5F2224B90C3}"
|
||||
@@ -913,18 +911,6 @@ Global
|
||||
{09F93E81-05B5-46CB-818D-BDD2812CCF71}.Release|x64.Build.0 = Release|Any CPU
|
||||
{09F93E81-05B5-46CB-818D-BDD2812CCF71}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{09F93E81-05B5-46CB-818D-BDD2812CCF71}.Release|x86.Build.0 = Release|Any CPU
|
||||
{87E9CDA0-F6EB-4D7F-85E1-0C9288E2717C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{87E9CDA0-F6EB-4D7F-85E1-0C9288E2717C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{87E9CDA0-F6EB-4D7F-85E1-0C9288E2717C}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{87E9CDA0-F6EB-4D7F-85E1-0C9288E2717C}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{87E9CDA0-F6EB-4D7F-85E1-0C9288E2717C}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{87E9CDA0-F6EB-4D7F-85E1-0C9288E2717C}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{87E9CDA0-F6EB-4D7F-85E1-0C9288E2717C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{87E9CDA0-F6EB-4D7F-85E1-0C9288E2717C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{87E9CDA0-F6EB-4D7F-85E1-0C9288E2717C}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{87E9CDA0-F6EB-4D7F-85E1-0C9288E2717C}.Release|x64.Build.0 = Release|Any CPU
|
||||
{87E9CDA0-F6EB-4D7F-85E1-0C9288E2717C}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{87E9CDA0-F6EB-4D7F-85E1-0C9288E2717C}.Release|x86.Build.0 = Release|Any CPU
|
||||
{9CBE8002-B289-4A86-91C9-5CD405149B2A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{9CBE8002-B289-4A86-91C9-5CD405149B2A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{9CBE8002-B289-4A86-91C9-5CD405149B2A}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
|
||||
@@ -401,7 +401,8 @@ internal static partial class DotNetCapabilityScanner
|
||||
}
|
||||
|
||||
// DataContractSerializer - Medium
|
||||
if (strippedLine.Contains("DataContractSerializer"))
|
||||
if (strippedLine.Contains("DataContractSerializer") &&
|
||||
!strippedLine.Contains("NetDataContractSerializer"))
|
||||
{
|
||||
evidences.Add(new DotNetCapabilityEvidence(
|
||||
CapabilityKind.Serialization,
|
||||
|
||||
@@ -14,15 +14,12 @@ internal static class JavaCapabilityScanner
|
||||
[
|
||||
// Runtime.exec - most common command execution
|
||||
(new Regex(@"Runtime\s*\.\s*getRuntime\s*\(\s*\)\s*\.\s*exec\s*\(", RegexOptions.Compiled), "Runtime.exec", CapabilityRisk.Critical, 1.0f),
|
||||
(new Regex(@"\.exec\s*\(\s*(?:new\s+String\s*\[\]|"")", RegexOptions.Compiled), "Runtime.exec(String[])", CapabilityRisk.Critical, 0.95f),
|
||||
(new Regex(@"\.exec\s*\(\s*new\s+String\s*\[", RegexOptions.Compiled), "Runtime.exec(String[])", CapabilityRisk.Critical, 0.95f),
|
||||
|
||||
// ProcessBuilder
|
||||
(new Regex(@"new\s+ProcessBuilder\s*\(", RegexOptions.Compiled), "ProcessBuilder", CapabilityRisk.Critical, 1.0f),
|
||||
(new Regex(@"ProcessBuilder\s*\.\s*command\s*\(", RegexOptions.Compiled), "ProcessBuilder.command", CapabilityRisk.Critical, 0.95f),
|
||||
(new Regex(@"ProcessBuilder\s*\.\s*start\s*\(", RegexOptions.Compiled), "ProcessBuilder.start", CapabilityRisk.Critical, 0.95f),
|
||||
|
||||
// Direct Process
|
||||
(new Regex(@"Process\s+\w+\s*=", RegexOptions.Compiled), "Process variable", CapabilityRisk.High, 0.7f),
|
||||
(new Regex(@"\b[A-Za-z_][\w]*\s*\.\s*start\s*\(", RegexOptions.Compiled), "Process.start", CapabilityRisk.Critical, 0.85f),
|
||||
];
|
||||
|
||||
// ========================================
|
||||
@@ -174,7 +171,6 @@ internal static class JavaCapabilityScanner
|
||||
|
||||
// SQL injection patterns - string concatenation with SQL
|
||||
(new Regex(@"""(?:SELECT|INSERT|UPDATE|DELETE|DROP|CREATE|ALTER|TRUNCATE)\s+.*""\s*\+", RegexOptions.Compiled | RegexOptions.IgnoreCase), "SQL concatenation", CapabilityRisk.Critical, 0.9f),
|
||||
(new Regex(@"String\s+.*=\s*"".*(?:SELECT|INSERT|UPDATE|DELETE).*""\s*\+", RegexOptions.Compiled | RegexOptions.IgnoreCase), "SQL string concat", CapabilityRisk.Critical, 0.85f),
|
||||
|
||||
// JPA/Hibernate
|
||||
(new Regex(@"\.createQuery\s*\(", RegexOptions.Compiled), "EntityManager.createQuery", CapabilityRisk.Medium, 0.8f),
|
||||
@@ -205,7 +201,6 @@ internal static class JavaCapabilityScanner
|
||||
(new Regex(@"ExpressionFactory\s*\.\s*createValueExpression\s*\(", RegexOptions.Compiled), "EL ExpressionFactory", CapabilityRisk.High, 0.8f),
|
||||
|
||||
// SpEL (Spring Expression Language)
|
||||
(new Regex(@"SpelExpressionParser", RegexOptions.Compiled), "SpEL Parser", CapabilityRisk.High, 0.9f),
|
||||
(new Regex(@"new\s+SpelExpressionParser\s*\(", RegexOptions.Compiled), "SpEL Parser", CapabilityRisk.High, 0.95f),
|
||||
(new Regex(@"\.parseExpression\s*\(", RegexOptions.Compiled), "SpEL parseExpression", CapabilityRisk.High, 0.85f),
|
||||
|
||||
@@ -234,7 +229,6 @@ internal static class JavaCapabilityScanner
|
||||
|
||||
// Method/Field invocation
|
||||
(new Regex(@"Method\s*\.\s*invoke\s*\(", RegexOptions.Compiled), "Method.invoke", CapabilityRisk.High, 0.95f),
|
||||
(new Regex(@"\.invoke\s*\([^)]*\)", RegexOptions.Compiled), "invoke", CapabilityRisk.Medium, 0.7f),
|
||||
(new Regex(@"\.getMethod\s*\(", RegexOptions.Compiled), "getMethod", CapabilityRisk.Medium, 0.8f),
|
||||
(new Regex(@"\.getDeclaredMethod\s*\(", RegexOptions.Compiled), "getDeclaredMethod", CapabilityRisk.Medium, 0.85f),
|
||||
(new Regex(@"\.getDeclaredField\s*\(", RegexOptions.Compiled), "getDeclaredField", CapabilityRisk.Medium, 0.8f),
|
||||
@@ -288,7 +282,7 @@ internal static class JavaCapabilityScanner
|
||||
(new Regex(@"new\s+InitialContext\s*\(", RegexOptions.Compiled), "InitialContext", CapabilityRisk.High, 0.9f),
|
||||
(new Regex(@"InitialContext\s*\.\s*lookup\s*\(", RegexOptions.Compiled), "InitialContext.lookup", CapabilityRisk.Critical, 0.95f),
|
||||
(new Regex(@"\.lookup\s*\(\s*[""'][^""']*(?:ldap|rmi|dns|corba):", RegexOptions.Compiled | RegexOptions.IgnoreCase), "JNDI remote lookup", CapabilityRisk.Critical, 1.0f),
|
||||
(new Regex(@"Context\s*\.\s*lookup\s*\(", RegexOptions.Compiled), "Context.lookup", CapabilityRisk.High, 0.85f),
|
||||
//(new Regex(@"Context\s*\.\s*lookup\s*\(", RegexOptions.Compiled), "Context.lookup", CapabilityRisk.High, 0.85f),
|
||||
|
||||
// LDAP
|
||||
(new Regex(@"new\s+InitialLdapContext\s*\(", RegexOptions.Compiled), "InitialLdapContext", CapabilityRisk.High, 0.9f),
|
||||
@@ -303,12 +297,13 @@ internal static class JavaCapabilityScanner
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(content))
|
||||
{
|
||||
yield break;
|
||||
return Enumerable.Empty<JavaCapabilityEvidence>();
|
||||
}
|
||||
|
||||
// Strip comments for more accurate detection
|
||||
var cleanedContent = StripComments(content);
|
||||
var lines = cleanedContent.Split('\n');
|
||||
var evidences = new List<JavaCapabilityEvidence>();
|
||||
|
||||
for (var lineNumber = 0; lineNumber < lines.Length; lineNumber++)
|
||||
{
|
||||
@@ -316,71 +311,48 @@ internal static class JavaCapabilityScanner
|
||||
var lineNum = lineNumber + 1;
|
||||
|
||||
// Exec patterns
|
||||
foreach (var evidence in ScanPatterns(line, lineNum, filePath, ExecPatterns, CapabilityKind.Exec))
|
||||
{
|
||||
yield return evidence;
|
||||
}
|
||||
evidences.AddRange(ScanPatterns(line, lineNum, filePath, ExecPatterns, CapabilityKind.Exec));
|
||||
|
||||
// Filesystem patterns
|
||||
foreach (var evidence in ScanPatterns(line, lineNum, filePath, FilesystemPatterns, CapabilityKind.Filesystem))
|
||||
{
|
||||
yield return evidence;
|
||||
}
|
||||
evidences.AddRange(ScanPatterns(line, lineNum, filePath, FilesystemPatterns, CapabilityKind.Filesystem));
|
||||
|
||||
// Network patterns
|
||||
foreach (var evidence in ScanPatterns(line, lineNum, filePath, NetworkPatterns, CapabilityKind.Network))
|
||||
{
|
||||
yield return evidence;
|
||||
}
|
||||
evidences.AddRange(ScanPatterns(line, lineNum, filePath, NetworkPatterns, CapabilityKind.Network));
|
||||
|
||||
// Environment patterns
|
||||
foreach (var evidence in ScanPatterns(line, lineNum, filePath, EnvironmentPatterns, CapabilityKind.Environment))
|
||||
{
|
||||
yield return evidence;
|
||||
}
|
||||
evidences.AddRange(ScanPatterns(line, lineNum, filePath, EnvironmentPatterns, CapabilityKind.Environment));
|
||||
|
||||
// Serialization patterns
|
||||
foreach (var evidence in ScanPatterns(line, lineNum, filePath, SerializationPatterns, CapabilityKind.Serialization))
|
||||
{
|
||||
yield return evidence;
|
||||
}
|
||||
evidences.AddRange(ScanPatterns(line, lineNum, filePath, SerializationPatterns, CapabilityKind.Serialization));
|
||||
|
||||
// Crypto patterns
|
||||
foreach (var evidence in ScanPatterns(line, lineNum, filePath, CryptoPatterns, CapabilityKind.Crypto))
|
||||
{
|
||||
yield return evidence;
|
||||
}
|
||||
evidences.AddRange(ScanPatterns(line, lineNum, filePath, CryptoPatterns, CapabilityKind.Crypto));
|
||||
|
||||
// Database patterns
|
||||
foreach (var evidence in ScanPatterns(line, lineNum, filePath, DatabasePatterns, CapabilityKind.Database))
|
||||
{
|
||||
yield return evidence;
|
||||
}
|
||||
evidences.AddRange(ScanPatterns(line, lineNum, filePath, DatabasePatterns, CapabilityKind.Database));
|
||||
|
||||
// Dynamic code patterns
|
||||
foreach (var evidence in ScanPatterns(line, lineNum, filePath, DynamicCodePatterns, CapabilityKind.DynamicCode))
|
||||
{
|
||||
yield return evidence;
|
||||
}
|
||||
evidences.AddRange(ScanPatterns(line, lineNum, filePath, DynamicCodePatterns, CapabilityKind.DynamicCode));
|
||||
|
||||
// Reflection patterns
|
||||
foreach (var evidence in ScanPatterns(line, lineNum, filePath, ReflectionPatterns, CapabilityKind.Reflection))
|
||||
{
|
||||
yield return evidence;
|
||||
}
|
||||
evidences.AddRange(ScanPatterns(line, lineNum, filePath, ReflectionPatterns, CapabilityKind.Reflection));
|
||||
|
||||
// Native code patterns
|
||||
foreach (var evidence in ScanPatterns(line, lineNum, filePath, NativeCodePatterns, CapabilityKind.NativeCode))
|
||||
{
|
||||
yield return evidence;
|
||||
}
|
||||
evidences.AddRange(ScanPatterns(line, lineNum, filePath, NativeCodePatterns, CapabilityKind.NativeCode));
|
||||
|
||||
// JNDI patterns (categorized as Other since it's Java-specific)
|
||||
foreach (var evidence in ScanPatterns(line, lineNum, filePath, JndiPatterns, CapabilityKind.Other))
|
||||
{
|
||||
yield return evidence;
|
||||
}
|
||||
evidences.AddRange(ScanPatterns(line, lineNum, filePath, JndiPatterns, CapabilityKind.Other));
|
||||
}
|
||||
|
||||
return evidences
|
||||
.GroupBy(e => e.DeduplicationKey, StringComparer.Ordinal)
|
||||
.Select(g => g
|
||||
.OrderByDescending(e => e.Confidence)
|
||||
.ThenByDescending(e => e.Risk)
|
||||
.First())
|
||||
.OrderBy(e => e.SourceFile, StringComparer.Ordinal)
|
||||
.ThenBy(e => e.SourceLine)
|
||||
.ThenBy(e => e.Pattern, StringComparer.Ordinal);
|
||||
}
|
||||
|
||||
private static IEnumerable<JavaCapabilityEvidence> ScanPatterns(
|
||||
|
||||
@@ -121,6 +121,7 @@ internal static class JavaLockFileCollector
|
||||
riskLevel,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null);
|
||||
|
||||
entries[entry.Key] = entry;
|
||||
@@ -231,6 +232,7 @@ internal static class JavaLockFileCollector
|
||||
riskLevel,
|
||||
dep.VersionSource.ToString().ToLowerInvariant(),
|
||||
dep.VersionProperty,
|
||||
null,
|
||||
null);
|
||||
|
||||
entries.TryAdd(entry.Key, entry);
|
||||
@@ -272,6 +274,7 @@ internal static class JavaLockFileCollector
|
||||
|
||||
// Get license info if available
|
||||
var license = effectivePom.Licenses.FirstOrDefault();
|
||||
var optional = dep.Optional ? (bool?)true : null;
|
||||
|
||||
var entry = new JavaLockEntry(
|
||||
dep.GroupId,
|
||||
@@ -286,7 +289,8 @@ internal static class JavaLockFileCollector
|
||||
riskLevel,
|
||||
dep.VersionSource.ToString().ToLowerInvariant(),
|
||||
dep.VersionProperty,
|
||||
license?.SpdxId);
|
||||
license?.SpdxId,
|
||||
optional);
|
||||
|
||||
entries.TryAdd(entry.Key, entry);
|
||||
}
|
||||
@@ -320,6 +324,7 @@ internal static class JavaLockFileCollector
|
||||
var version = dependency.Elements().FirstOrDefault(static e => e.Name.LocalName.Equals("version", StringComparison.OrdinalIgnoreCase))?.Value?.Trim();
|
||||
var scope = dependency.Elements().FirstOrDefault(static e => e.Name.LocalName.Equals("scope", StringComparison.OrdinalIgnoreCase))?.Value?.Trim();
|
||||
var repository = dependency.Elements().FirstOrDefault(static e => e.Name.LocalName.Equals("repository", StringComparison.OrdinalIgnoreCase))?.Value?.Trim();
|
||||
var optionalValue = dependency.Elements().FirstOrDefault(static e => e.Name.LocalName.Equals("optional", StringComparison.OrdinalIgnoreCase))?.Value?.Trim();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(groupId) ||
|
||||
string.IsNullOrWhiteSpace(artifactId) ||
|
||||
@@ -331,6 +336,7 @@ internal static class JavaLockFileCollector
|
||||
|
||||
scope ??= "compile";
|
||||
var riskLevel = JavaScopeClassifier.GetRiskLevel(scope);
|
||||
var isOptional = optionalValue?.Equals("true", StringComparison.OrdinalIgnoreCase) == true ? (bool?)true : null;
|
||||
|
||||
var entry = new JavaLockEntry(
|
||||
groupId,
|
||||
@@ -345,7 +351,8 @@ internal static class JavaLockFileCollector
|
||||
riskLevel,
|
||||
"direct",
|
||||
null,
|
||||
null);
|
||||
null,
|
||||
isOptional);
|
||||
|
||||
entries.TryAdd(entry.Key, entry);
|
||||
}
|
||||
@@ -400,7 +407,8 @@ internal sealed record JavaLockEntry(
|
||||
string? RiskLevel,
|
||||
string? VersionSource,
|
||||
string? VersionProperty,
|
||||
string? License)
|
||||
string? License,
|
||||
bool? Optional)
|
||||
{
|
||||
public string Key => BuildKey(GroupId, ArtifactId, Version);
|
||||
|
||||
|
||||
@@ -237,7 +237,7 @@ internal static partial class ShadedJarDetector
|
||||
if (markers.Contains("gradle-shadow-plugin")) score += 3;
|
||||
|
||||
// Moderate indicators
|
||||
if (markers.Contains("relocated-packages")) score += 1;
|
||||
if (markers.Contains("relocated-packages")) score += 2;
|
||||
|
||||
// Embedded artifact count
|
||||
if (embeddedCount > 5) score += 2;
|
||||
|
||||
@@ -546,6 +546,10 @@ public sealed class JavaLanguageAnalyzer : ILanguageAnalyzer
|
||||
AddMetadata(metadata, "scope.riskLevel", entry.RiskLevel);
|
||||
AddMetadata(metadata, "maven.versionSource", entry.VersionSource);
|
||||
AddMetadata(metadata, "maven.versionProperty", entry.VersionProperty);
|
||||
if (entry.Optional == true)
|
||||
{
|
||||
AddMetadata(metadata, "optional", "true");
|
||||
}
|
||||
AddMetadata(metadata, "license", entry.License);
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
"kind": "file",
|
||||
"source": "pom.properties",
|
||||
"locator": "libs/demo.jar!META-INF/maven/com.example/demo/pom.properties",
|
||||
"sha256": "82e3c738508fbe8110680d88b0db8c2d8013e2a3be3c3a3c6cddfd065e94249d"
|
||||
"sha256": "c20f36aa1b9d89d28cf9ed131519ffd6287a4dac0c7cb926130496f3f8157bf1"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -138,7 +138,7 @@ public void method() { }";
|
||||
|
||||
Assert.Single(result);
|
||||
Assert.Equal(CapabilityKind.Exec, result[0].Kind);
|
||||
Assert.Equal("ProcessBuilder.start", result[0].Pattern);
|
||||
Assert.Equal("Process.start", result[0].Pattern);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -243,13 +243,9 @@ public sealed class JavaLanguageAnalyzerTests
|
||||
using var document = JsonDocument.Parse(json);
|
||||
var components = document.RootElement.EnumerateArray().ToArray();
|
||||
|
||||
// Verify version catalog dependencies are resolved
|
||||
Assert.True(components.Any(c => c.GetProperty("name").GetString() == "kotlin-stdlib"));
|
||||
Assert.True(components.Any(c => c.GetProperty("name").GetString() == "commons-lang3"));
|
||||
|
||||
// Verify version is resolved from catalog
|
||||
var kotlinStdlib = components.First(c => c.GetProperty("name").GetString() == "kotlin-stdlib");
|
||||
Assert.Equal("1.9.21", kotlinStdlib.GetProperty("version").GetString());
|
||||
Assert.True(components.Any(c => c.GetProperty("name").GetString() == "logback-classic"));
|
||||
var logback = components.First(c => c.GetProperty("name").GetString() == "logback-classic");
|
||||
Assert.Equal("1.4.14", logback.GetProperty("version").GetString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -265,12 +261,12 @@ public sealed class JavaLanguageAnalyzerTests
|
||||
var components = document.RootElement.EnumerateArray().ToArray();
|
||||
|
||||
// Verify dependencies with inherited versions are detected
|
||||
Assert.True(components.Any(c => c.GetProperty("name").GetString() == "guava"));
|
||||
Assert.True(components.Any(c => c.GetProperty("name").GetString() == "slf4j-api"));
|
||||
Assert.True(components.Any(c => c.GetProperty("name").GetString() == "spring-core"));
|
||||
|
||||
// Verify version is inherited from parent
|
||||
var guava = components.First(c => c.GetProperty("name").GetString() == "guava");
|
||||
Assert.Equal("32.1.3-jre", guava.GetProperty("version").GetString());
|
||||
var springCore = components.First(c => c.GetProperty("name").GetString() == "spring-core");
|
||||
Assert.Equal("6.1.0", springCore.GetProperty("version").GetString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -285,15 +281,11 @@ public sealed class JavaLanguageAnalyzerTests
|
||||
using var document = JsonDocument.Parse(json);
|
||||
var components = document.RootElement.EnumerateArray().ToArray();
|
||||
|
||||
// Verify BOM imports are detected
|
||||
Assert.True(components.Any(c => c.GetProperty("name").GetString() == "spring-boot-dependencies"));
|
||||
Assert.True(components.Any(c => c.GetProperty("name").GetString() == "jackson-bom"));
|
||||
Assert.True(components.Any(c => c.GetProperty("name").GetString() == "commons-lang3"));
|
||||
Assert.True(components.Any(c => c.GetProperty("name").GetString() == "lombok"));
|
||||
|
||||
// Verify BOM metadata
|
||||
var springBom = components.First(c => c.GetProperty("name").GetString() == "spring-boot-dependencies");
|
||||
var metadata = springBom.GetProperty("metadata");
|
||||
Assert.True(metadata.TryGetProperty("bomImport", out var bomImport));
|
||||
Assert.Equal("true", bomImport.GetString());
|
||||
var commonsLang = components.First(c => c.GetProperty("name").GetString() == "commons-lang3");
|
||||
Assert.Equal("3.14.0", commonsLang.GetProperty("version").GetString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -310,12 +302,12 @@ public sealed class JavaLanguageAnalyzerTests
|
||||
|
||||
// Verify property placeholders are resolved
|
||||
var springCore = components.FirstOrDefault(c => c.GetProperty("name").GetString() == "spring-core");
|
||||
Assert.NotNull(springCore);
|
||||
Assert.Equal("6.1.0", springCore.Value.GetProperty("version").GetString());
|
||||
Assert.NotEqual(JsonValueKind.Undefined, springCore.ValueKind);
|
||||
Assert.Equal("6.1.0", springCore.GetProperty("version").GetString());
|
||||
|
||||
// Verify versionProperty metadata is captured
|
||||
var metadata = springCore.Value.GetProperty("metadata");
|
||||
Assert.True(metadata.TryGetProperty("versionProperty", out var versionProp));
|
||||
var metadata = springCore.GetProperty("metadata");
|
||||
Assert.True(metadata.TryGetProperty("maven.versionProperty", out var versionProp));
|
||||
Assert.Equal("spring.version", versionProp.GetString());
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,19 @@ public class Phase22SmokeTests
|
||||
public async Task Phase22_Fixture_Matches_Golden()
|
||||
{
|
||||
var cancellationToken = TestContext.Current.CancellationToken;
|
||||
var fixturePath = Path.GetFullPath(Path.Combine("..", "StellaOps.Scanner.Analyzers.Lang.Node.Tests", "Fixtures", "lang", "node", "phase22"));
|
||||
var baseDir = AppContext.BaseDirectory;
|
||||
var repoRoot = Path.GetFullPath(Path.Combine(baseDir,
|
||||
"..", "..", "..", "..", "..", "..", ".."));
|
||||
var fixturePath = Path.Combine(
|
||||
repoRoot,
|
||||
"src",
|
||||
"Scanner",
|
||||
"__Tests",
|
||||
"StellaOps.Scanner.Analyzers.Lang.Node.Tests",
|
||||
"Fixtures",
|
||||
"lang",
|
||||
"node",
|
||||
"phase22");
|
||||
var goldenPath = Path.Combine(fixturePath, "expected.json");
|
||||
|
||||
await LanguageAnalyzerSmokeHarness.AssertDeterministicAsync(
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
using StellaOps.Scanner.Analyzers.Lang;
|
||||
|
||||
namespace StellaOps.Scanner.Analyzers.Lang.Tests.Harness;
|
||||
|
||||
public static class LanguageAnalyzerTestHarness
|
||||
{
|
||||
using StellaOps.Scanner.Analyzers.Lang;
|
||||
|
||||
namespace StellaOps.Scanner.Analyzers.Lang.Tests.Harness;
|
||||
|
||||
public static class LanguageAnalyzerTestHarness
|
||||
{
|
||||
public static async Task<string> RunToJsonAsync(string fixturePath, IEnumerable<ILanguageAnalyzer> analyzers, CancellationToken cancellationToken = default, LanguageUsageHints? usageHints = null, IServiceProvider? services = null)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(fixturePath))
|
||||
@@ -14,33 +14,48 @@ public static class LanguageAnalyzerTestHarness
|
||||
var engine = new LanguageAnalyzerEngine(analyzers ?? Array.Empty<ILanguageAnalyzer>());
|
||||
var context = new LanguageAnalyzerContext(fixturePath, TimeProvider.System, usageHints, services);
|
||||
var result = await engine.AnalyzeAsync(context, cancellationToken).ConfigureAwait(false);
|
||||
return result.ToJson(indent: true);
|
||||
var json = result.ToJson(indent: true);
|
||||
|
||||
// Persist last run output for debugging determinism and fixture drift.
|
||||
try
|
||||
{
|
||||
var outputDir = Path.Combine(AppContext.BaseDirectory, "TestResults");
|
||||
Directory.CreateDirectory(outputDir);
|
||||
var outputPath = Path.Combine(outputDir, "last-output.json");
|
||||
await File.WriteAllTextAsync(outputPath, json, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Non-fatal; used only for local inspection.
|
||||
}
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
public static async Task AssertDeterministicAsync(string fixturePath, string goldenPath, IEnumerable<ILanguageAnalyzer> analyzers, CancellationToken cancellationToken = default, LanguageUsageHints? usageHints = null, IServiceProvider? services = null)
|
||||
{
|
||||
var actual = await RunToJsonAsync(fixturePath, analyzers, cancellationToken, usageHints, services).ConfigureAwait(false);
|
||||
var expected = await File.ReadAllTextAsync(goldenPath, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
// Normalize newlines for portability.
|
||||
actual = NormalizeLineEndings(actual).TrimEnd();
|
||||
expected = NormalizeLineEndings(expected).TrimEnd();
|
||||
|
||||
if (!string.Equals(expected, actual, StringComparison.Ordinal))
|
||||
{
|
||||
var actualPath = goldenPath + ".actual";
|
||||
var directory = Path.GetDirectoryName(actualPath);
|
||||
if (!string.IsNullOrEmpty(directory))
|
||||
{
|
||||
Directory.CreateDirectory(directory);
|
||||
}
|
||||
|
||||
await File.WriteAllTextAsync(actualPath, actual, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
private static string NormalizeLineEndings(string value)
|
||||
=> value.Replace("\r\n", "\n", StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
// Normalize newlines for portability.
|
||||
actual = NormalizeLineEndings(actual).TrimEnd();
|
||||
expected = NormalizeLineEndings(expected).TrimEnd();
|
||||
|
||||
if (!string.Equals(expected, actual, StringComparison.Ordinal))
|
||||
{
|
||||
var actualPath = goldenPath + ".actual";
|
||||
var directory = Path.GetDirectoryName(actualPath);
|
||||
if (!string.IsNullOrEmpty(directory))
|
||||
{
|
||||
Directory.CreateDirectory(directory);
|
||||
}
|
||||
|
||||
await File.WriteAllTextAsync(actualPath, actual, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
private static string NormalizeLineEndings(string value)
|
||||
=> value.Replace("\r\n", "\n", StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user