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-grpc-sql:302"
language: go
project: grpc-sql
version: "1.0.0"
description: "SQL injection sink reachable via gRPC GetUser method"
entrypoints:
- "grpc:UserService.GetUser"
sinks:
- id: "SqlInjection::GetUser"
path: "main.(*userServer).GetUser"
kind: "custom"
location:
file: main.go
line: 35
notes: "database/sql.Query with string concatenation"
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: "SQL injection reachable"
evidence_files:
- "../benchmark/truth/go-grpc-sql.json"
sandbox:
network: loopback
privileges: rootless
redaction:
pii: false
policy: "benchmark-default/v1"

View File

@@ -0,0 +1,8 @@
case_id: "go-grpc-sql:302"
entries:
grpc:
- id: "grpc:UserService.GetUser"
service: "UserService"
method: "GetUser"
handler: "main.(*userServer).GetUser"
description: "Fetches user by ID with SQL injection vulnerability"

View File

@@ -0,0 +1,8 @@
module grpc-sql
go 1.22
require (
google.golang.org/grpc v1.64.0
google.golang.org/protobuf v1.34.2
)

View File

@@ -0,0 +1,86 @@
// grpc-sql benchmark case
// Demonstrates SQL injection sink reachable via gRPC handler
package main
import (
"context"
"database/sql"
"fmt"
"log"
"net"
_ "github.com/mattn/go-sqlite3"
"google.golang.org/grpc"
)
// User represents a user record
type User struct {
ID string
Name string
Email string
}
// userServer implements the gRPC UserService
type userServer struct {
db *sql.DB
}
// GetUser - VULNERABLE: SQL injection sink
// User ID is concatenated directly into SQL query
func (s *userServer) GetUser(ctx context.Context, userID string) (*User, error) {
// SINK: database/sql.Query with string concatenation
query := fmt.Sprintf("SELECT id, name, email FROM users WHERE id = '%s'", userID)
row := s.db.QueryRow(query)
var user User
if err := row.Scan(&user.ID, &user.Name, &user.Email); err != nil {
return nil, fmt.Errorf("user not found: %w", err)
}
return &user, nil
}
// GetUserSafe - SAFE: uses parameterized query
func (s *userServer) GetUserSafe(ctx context.Context, userID string) (*User, error) {
query := "SELECT id, name, email FROM users WHERE id = ?"
row := s.db.QueryRow(query, userID)
var user User
if err := row.Scan(&user.ID, &user.Name, &user.Email); err != nil {
return nil, fmt.Errorf("user not found: %w", err)
}
return &user, nil
}
func main() {
db, err := sql.Open("sqlite3", ":memory:")
if err != nil {
log.Fatalf("failed to open database: %v", err)
}
defer db.Close()
// Initialize schema
_, err = db.Exec(`
CREATE TABLE users (
id TEXT PRIMARY KEY,
name TEXT,
email TEXT
)
`)
if err != nil {
log.Fatalf("failed to create table: %v", err)
}
lis, err := net.Listen("tcp", ":50051")
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
s := grpc.NewServer()
// Register service here (simplified for benchmark)
log.Printf("gRPC server listening on %v", lis.Addr())
if err := s.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}

View File

@@ -0,0 +1,56 @@
package main
import (
"context"
"database/sql"
"testing"
_ "github.com/mattn/go-sqlite3"
)
func setupTestDB(t *testing.T) *sql.DB {
db, err := sql.Open("sqlite3", ":memory:")
if err != nil {
t.Fatalf("failed to open database: %v", err)
}
_, err = db.Exec(`
CREATE TABLE users (
id TEXT PRIMARY KEY,
name TEXT,
email TEXT
);
INSERT INTO users (id, name, email) VALUES ('1', 'Alice', 'alice@example.com');
`)
if err != nil {
t.Fatalf("failed to setup test data: %v", err)
}
return db
}
func TestGetUserSafe(t *testing.T) {
db := setupTestDB(t)
defer db.Close()
server := &userServer{db: db}
user, err := server.GetUserSafe(context.Background(), "1")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if user.Name != "Alice" {
t.Errorf("expected Alice, got %s", user.Name)
}
}
func TestGetUserNotFound(t *testing.T) {
db := setupTestDB(t)
defer db.Close()
server := &userServer{db: db}
_, err := server.GetUserSafe(context.Background(), "999")
if err == nil {
t.Error("expected error for non-existent user")
}
}

View File

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