feat: add security sink detection patterns for JavaScript/TypeScript

- Introduced `sink-detect.js` with various security sink detection patterns categorized by type (e.g., command injection, SQL injection, file operations).
- Implemented functions to build a lookup map for fast sink detection and to match sink calls against known patterns.
- Added `package-lock.json` for dependency management.
This commit is contained in:
StellaOps Bot
2025-12-22 23:21:21 +02:00
parent 3ba7157b00
commit 5146204f1b
529 changed files with 73579 additions and 5985 deletions

View File

@@ -183,4 +183,158 @@ public class NodeCallGraphExtractorTests
Assert.Equal("/users/:id", ep.Route);
Assert.Equal("GET", ep.Method);
}
[Fact]
public void BabelResultParser_ParsesSinks()
{
// Arrange
var json = """
{
"module": "test",
"nodes": [
{
"id": "js:test/handler.processRequest",
"package": "test",
"name": "processRequest"
}
],
"edges": [],
"entrypoints": [],
"sinks": [
{
"caller": "js:test/handler.processRequest",
"category": "command_injection",
"method": "child_process.exec",
"site": {
"file": "handler.js",
"line": 42,
"column": 8
}
}
]
}
""";
// Act
var result = BabelResultParser.Parse(json);
// Assert
Assert.Single(result.Sinks);
var sink = result.Sinks[0];
Assert.Equal("js:test/handler.processRequest", sink.Caller);
Assert.Equal("command_injection", sink.Category);
Assert.Equal("child_process.exec", sink.Method);
Assert.NotNull(sink.Site);
Assert.Equal("handler.js", sink.Site.File);
Assert.Equal(42, sink.Site.Line);
}
[Fact]
public void BabelResultParser_ParsesMultipleSinkCategories()
{
// Arrange
var json = """
{
"module": "vulnerable-app",
"nodes": [],
"edges": [],
"entrypoints": [],
"sinks": [
{
"caller": "js:vulnerable-app/db.query",
"category": "sql_injection",
"method": "connection.query"
},
{
"caller": "js:vulnerable-app/api.fetch",
"category": "ssrf",
"method": "fetch"
},
{
"caller": "js:vulnerable-app/file.write",
"category": "file_write",
"method": "fs.writeFileSync"
}
]
}
""";
// Act
var result = BabelResultParser.Parse(json);
// Assert
Assert.Equal(3, result.Sinks.Count);
Assert.Contains(result.Sinks, s => s.Category == "sql_injection");
Assert.Contains(result.Sinks, s => s.Category == "ssrf");
Assert.Contains(result.Sinks, s => s.Category == "file_write");
}
[Fact]
public void BabelResultParser_ParsesEmptySinks()
{
// Arrange
var json = """
{
"module": "safe-app",
"nodes": [],
"edges": [],
"entrypoints": [],
"sinks": []
}
""";
// Act
var result = BabelResultParser.Parse(json);
// Assert
Assert.Empty(result.Sinks);
}
[Fact]
public void BabelResultParser_ParsesMissingSinks()
{
// Arrange - sinks field omitted entirely
var json = """
{
"module": "legacy-app",
"nodes": [],
"edges": [],
"entrypoints": []
}
""";
// Act
var result = BabelResultParser.Parse(json);
// Assert - should default to empty list
Assert.Empty(result.Sinks);
}
[Fact]
public void BabelResultParser_ParsesSinkWithoutSite()
{
// Arrange
var json = """
{
"module": "test",
"nodes": [],
"edges": [],
"entrypoints": [],
"sinks": [
{
"caller": "js:test/func",
"category": "deserialization",
"method": "eval"
}
]
}
""";
// Act
var result = BabelResultParser.Parse(json);
// Assert
Assert.Single(result.Sinks);
Assert.Null(result.Sinks[0].Site);
}
}