feat: add stella-callgraph-node for JavaScript/TypeScript call graph extraction

- Implemented a new tool `stella-callgraph-node` that extracts call graphs from JavaScript/TypeScript projects using Babel AST.
- Added command-line interface with options for JSON output and help.
- Included functionality to analyze project structure, detect functions, and build call graphs.
- Created a package.json file for dependency management.

feat: introduce stella-callgraph-python for Python call graph extraction

- Developed `stella-callgraph-python` to extract call graphs from Python projects using AST analysis.
- Implemented command-line interface with options for JSON output and verbose logging.
- Added framework detection to identify popular web frameworks and their entry points.
- Created an AST analyzer to traverse Python code and extract function definitions and calls.
- Included requirements.txt for project dependencies.

chore: add framework detection for Python projects

- Implemented framework detection logic to identify frameworks like Flask, FastAPI, Django, and others based on project files and import patterns.
- Enhanced the AST analyzer to recognize entry points based on decorators and function definitions.
This commit is contained in:
master
2025-12-19 18:11:59 +02:00
parent 951a38d561
commit 8779e9226f
130 changed files with 19011 additions and 422 deletions

View File

@@ -0,0 +1,46 @@
id: "go-gin-exec:301"
language: go
project: gin-exec
version: "1.0.0"
description: "Command injection sink reachable via GET /run in Gin handler"
entrypoints:
- "GET /run"
sinks:
- id: "CommandInjection::handleRun"
path: "main.handleRun"
kind: "custom"
location:
file: main.go
line: 22
notes: "os/exec.Command with user-controlled input"
environment:
os_image: "golang:1.22-alpine"
runtime:
go: "1.22"
source_date_epoch: 1730000000
resource_limits:
cpu: "2"
memory: "2Gi"
build:
command: "go build -o outputs/app ."
source_date_epoch: 1730000000
outputs:
artifact_path: outputs/app
sbom_path: outputs/sbom.cdx.json
coverage_path: outputs/coverage.json
traces_dir: outputs/traces
attestation_path: outputs/attestation.json
test:
command: "go test -v ./..."
expected_coverage: []
expected_traces: []
ground_truth:
summary: "Command injection reachable"
evidence_files:
- "../benchmark/truth/go-gin-exec.json"
sandbox:
network: loopback
privileges: rootless
redaction:
pii: false
policy: "benchmark-default/v1"

View File

@@ -0,0 +1,8 @@
case_id: "go-gin-exec:301"
entries:
http:
- id: "GET /run"
route: "/run"
method: "GET"
handler: "main.handleRun"
description: "Executes shell command from query parameter"

View File

@@ -0,0 +1,5 @@
module gin-exec
go 1.22
require github.com/gin-gonic/gin v1.10.0

View File

@@ -0,0 +1,41 @@
// gin-exec benchmark case
// Demonstrates command injection sink reachable via Gin HTTP handler
package main
import (
"net/http"
"os/exec"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
r.GET("/run", handleRun)
r.GET("/health", handleHealth)
r.Run(":8080")
}
// handleRun - VULNERABLE: command injection sink
// User-controlled input passed directly to exec.Command
func handleRun(c *gin.Context) {
cmd := c.Query("cmd")
if cmd == "" {
c.JSON(http.StatusBadRequest, gin.H{"error": "missing cmd parameter"})
return
}
// SINK: os/exec.Command with user-controlled input
output, err := exec.Command("sh", "-c", cmd).Output()
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{"output": string(output)})
}
// handleHealth - safe endpoint, no sinks
func handleHealth(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"status": "ok"})
}

View File

@@ -0,0 +1,37 @@
package main
import (
"net/http"
"net/http/httptest"
"testing"
"github.com/gin-gonic/gin"
)
func TestHandleHealth(t *testing.T) {
gin.SetMode(gin.TestMode)
r := gin.Default()
r.GET("/health", handleHealth)
req, _ := http.NewRequest("GET", "/health", nil)
w := httptest.NewRecorder()
r.ServeHTTP(w, req)
if w.Code != http.StatusOK {
t.Errorf("Expected status 200, got %d", w.Code)
}
}
func TestHandleRunMissingCmd(t *testing.T) {
gin.SetMode(gin.TestMode)
r := gin.Default()
r.GET("/run", handleRun)
req, _ := http.NewRequest("GET", "/run", nil)
w := httptest.NewRecorder()
r.ServeHTTP(w, req)
if w.Code != http.StatusBadRequest {
t.Errorf("Expected status 400, got %d", w.Code)
}
}

View File

@@ -0,0 +1 @@
# Keep this directory for build outputs