
소스 코드의 그래프 데이터베이스 표현을 구축하고 쿼리합니다
소스 코드를 파싱하여 함수, 클래스, 호출 및 보안 분석을 위한 의미 주석의 쿼리 가능한 그래프로 만듭니다.
Trailmark는 언어에 구애받지 않는 AST 파싱을 위해 tree-sitter를 사용하고, 고성능 그래프 탐색을 위해 rustworkx를 사용합니다. 장기적인 비전은 이 그래프를 변이 테스트 및 커버리지 가이드 퍼징과 결합하여 사용자 입력으로부터 접근 가능한 가정과 테스트 커버리지 사이의 격차를 식별하는 것입니다.
Trailmark는 파싱, 인덱싱, 쿼리의 세 단계로 작동합니다.```mermaid flowchart TD A["Source Files"] --> B["tree-sitter Parser"] B --> C["CodeGraph (nodes + edges)"] C --> D["rustworkx GraphStore"] D --> E["QueryEngine"] E --> F["JSON / Summary / Hotspots"]
classDef src fill:#007bff26,stroke:#007bff,color:#007bff
classDef parse fill:#28a74526,stroke:#28a745,color:#28a745
classDef data fill:#6f42c126,stroke:#6f42c1,color:#6f42c1
classDef query fill:#ffc10726,stroke:#e6a817,color:#e6a817
class A src
class B parse
class C,D data
class E,F query
### 1. 파싱
언어별 파서가 디렉터리를 탐색하고 각 파일을 tree-sitter AST로 파싱하여 다음을 추출합니다:
- **노드** — 함수, 메서드, 클래스, 구조체, 인터페이스, 트레이트, 열거형, 모듈, 네임스페이스
- **에지** — 호출, 상속, 구현, 포함, 임포트
- **메타데이터** — 타입 어노테이션, 순환 복잡도, 분기, 독스트링, 예외 타입
### 지원 언어
| 언어 | 확장자 | 주요 구조 |
| --- | --- | --- |
| Python | `.py` | 함수, 클래스, 메서드 |
| JavaScript | `.js`, `.jsx`, `.mjs`, `.cjs` | 함수, 클래스, 화살표 함수 |
| TypeScript | `.ts`, `.tsx` | 함수, 클래스, 인터페이스, 열거형 |
| PHP | `.php` | 함수, 클래스, 인터페이스, 트레이트 |
| Ruby | `.rb` | 메서드, 클래스, 모듈 |
| C | `.c`, `.h` | 함수, 구조체, 열거형 |
| C++ | `.cpp`, `.hpp`, `.cc`, `.hh`, `.cxx`, `.hxx` | 함수, 클래스, 구조체, 네임스페이스 |
| C# | `.cs` | 메서드, 클래스, 인터페이스, 구조체, 열거형, 네임스페이스 |
| Java | `.java` | 메서드, 클래스, 인터페이스, 열거형 |
| Go | `.go` | 함수, 메서드, 구조체, 인터페이스 |
| Rust | `.rs` | 함수, 구조체, 트레이트, 열거형, impl 블록 |
| Solidity | `.sol` | 컨트랙트, 인터페이스, 라이브러리, 함수, 모디파이어, 구조체, 열거형 |
| Cairo | `.cairo` | 함수, 트레이트, 구조체, 열거형, impl 블록, StarkNet 컨트랙트 |
| Circom | `.circom` | 템플릿, 함수, 시그널, 컴포넌트 |
| Haskell | `.hs` | 함수, 데이터 타입, 타입 클래스, 인스턴스 |
| Erlang | `.erl` | 함수, 레코드, 비헤이비어, 모듈 |
| Miden Assembly | `.masm` | 프로시저, 진입점, 상수, 호출 |
| Swift | `.swift` | 함수, 클래스, 구조체, 열거형, 프로토콜, 익스텐션 |
| Objective-C | `.m`, `.mm`, `.h` | C 함수, 클래스, 메서드 (셀렉터 기반 명명) |
| Kotlin | `.kt`, `.kts` | 함수, 클래스, 인터페이스, 데이터 클래스, 객체, 메서드 |
| Dart | `.dart` | 함수, 클래스, 추상 클래스, 메서드, 생성자 |
| Move | `.move` | 모듈, 함수, 임포트, 직접 호출 |
| Tact | `.tact` | 컨트랙트, 구조체, 리시버, 함수 |
| Func | `.fc`, `.func` | 함수, 포함, 직접 호출 |
| Sway | `.sw` | ABI 인터페이스, 구조체, impl 메서드, 함수 |
| Rego | `.rego` | 패키지, 임포트, 정책 규칙, 규칙 호출 |
| Proto | `.proto` | 서비스, RPC, 메시지, 필드, 열거형 |
| Thrift | `.thrift` | 서비스, 함수, 구조체, 필드, 열거형 |
| GraphQL | `.graphql`, `.gql` | 객체 타입, 루트 연산, 필드, 열거형 |
| SQL | `.sql` | 스키마, 테이블, 뷰, 함수, 프로시저 |```mermaid
flowchart TD
subgraph "Per-File Parsing"
F["Source file"] --> TS["tree-sitter AST"]
TS --> EX["Extract nodes"]
TS --> EC["Extract call edges"]
TS --> EB["Count branches"]
TS --> ET["Resolve types"]
end
EX --> CG["CodeGraph"]
EC --> CG
EB --> CG
ET --> CG
classDef src fill:#007bff26,stroke:#007bff,color:#007bff
classDef parse fill:#28a74526,stroke:#28a745,color:#28a745
classDef extract fill:#ffc10726,stroke:#e6a817,color:#e6a817
classDef data fill:#6f42c126,stroke:#6f42c1,color:#6f42c1
class F src
class TS parse
class EX,EC,EB,ET extract
class CG data
Node ID는 module:function, module:Class, module:Class.method 스키마를 따르며 명확한 조회를 제공합니다. 디렉터리 파싱은 고유한 정의가 존재할 때 bare cross-file 호출을 해결합니다. 모호한 cross-file 호출은 원래 최선의 대상에 남겨두고 uncertain으로 표시됩니다. 에지 신뢰도는 certain(직접 호출, self.method()), inferred(self가 아닌 객체의 속성 접근), 또는 uncertain(동적 디스패치 또는 모호한 해석)으로 태깅됩니다.
GraphStore는 CodeGraph를 rustworkx PyDiGraph에 로드하고 빠른 탐색을 위해 양방향 ID/인덱스 매핑을 구축합니다.
QueryEngine은 인덱싱된 그래프에 대한 고급 API를 제공합니다:
| 메서드 | 설명 |
|---|---|
callers_of(name) | 지정된 대상의 직접 호출자 |
callees_of(name) | 지정된 소스의 직접 피호출자 |
ancestors_of(name) | 전이적으로 대상에 도달할 수 있는 모든 함수 (상향 슬라이스) |
reachable_from(name) | 소스에서 전이적으로 도달 가능한 모든 함수 |
paths_between(src, dst) | 두 노드 사이의 모든 단순 호출 경로 |
connect_subgraphs(source, target) | 두 명명된 서브그래프를 연결하는 경로 |
entrypoint_paths_to(name) | 감지된 진입점에서 대상까지의 경로 |
attack_surface() | 신뢰 수준, 자산 가치 및 존재할 때 파서 속성으로 태깅된 진입점 |
complexity_hotspots(n) | 순환 복잡도가 n 이상인 함수 |
functions_that_raise(exc) | 파서가 감지한 예외 목록에 exc를 포함하는 함수 |
generic_parameters(name) | 노드가 선언한 제네릭 타입 매개변수 |
type_references(name) | 매개변수, 반환, 예외 및 제네릭 경계 타입 참조 |
annotate(name, kind, description, source) | 노드에 시맨틱 주석 추가 |
annotations_of(name, kind=None) | 노드의 주석 가져오기, 선택적으로 종류별 필터링 |
nodes_with_annotation(kind) | 주어진 주석 종류로 태깅된 모든 노드 |
clear_annotations(name, kind=None) | 노드에서 주석 제거 |
diff_against(other) | 이 엔진의 그래프와 다른 그래프 간 구조적 차이 |
preanalysis() | 내장된 사전 분석 패스를 실행하고 주석/서브그래프 저장 |
augment_sarif(path) | SARIF 결과를 그래프에 병합 |
augment_weaudit(path) | weAudit 결과를 그래프에 병합 |
augment_binary(path) | 외부 바이너리 분석 그래프 JSON 파일 병합 |
findings(kind=None) | 발화 스타일 주석을 가진 노드 반환 |
subgraph(name) | 명명된 서브그래프의 노드 반환 |
subgraph_edges(name) | 명명된 서브그래프 내 유도 에지 반환 |
subgraph_names() | 현재 그래프에 있는 모든 명명된 서브그래프 나열 |
summary() | 노드 수, 에지 수, 종속성 |
to_json() | 전체 그래프 내보내기 |
classDiagram class CodeGraph { language: str root_path: str nodes: dict[str, CodeUnit] edges: list[CodeEdge] annotations: dict[str, list[Annotation]] entrypoints: dict[str, EntrypointTag] dependencies: list[str] add_annotation(node_id, annotation) clear_annotations(node_id, kind=None) merge(other) }
class CodeUnit {
id: str
name: str
kind: NodeKind
location: SourceLocation
parameters: tuple[Parameter]
return_type: TypeRef
exception_types: tuple[TypeRef]
cyclomatic_complexity: int
branches: tuple[BranchInfo]
docstring: str
}
class CodeEdge {
source_id: str
target_id: str
kind: EdgeKind
confidence: EdgeConfidence
}
class Annotation {
kind: AnnotationKind
description: str
source: str
}
class EntrypointTag {
kind: EntrypointKind
trust_level: TrustLevel
description: str
asset_value: AssetValue
}
CodeGraph "1" *-- "*" CodeUnit
CodeGraph "1" *-- "*" CodeEdge
CodeGraph "1" *-- "*" Annotation
CodeGraph "1" *-- "*" EntrypointTag
**노드 종류:** `function`, `method`, `class`, `module`, `struct`, `interface`, `trait`, `enum`, `namespace`, `contract`, `library`, `template`, `proxy`
**노드 출처:** `source`, `proxy`, `binary`, `synthetic`
**엣지 종류:** `calls`, `inherits`, `implements`, `contains`, `imports`, `resolves_to`, `type_uses`, `specializes`, `corresponds_to`
**엣지 신뢰도:** `certain`, `inferred`, `uncertain`
미해결 호출은 `proxy.unresolved:<raw-symbol>`과 같은 프록시 노드로 구체화되어, 트래버설 결과는 소스 분석이 해석을 잃어버린 위치를 자동으로 해당 엣지를 삭제하는 대신 보여줄 수 있습니다. 바이너리 분석은 외부 JSON 호출 그래프의 가져오기를 지원합니다. Trailmark는 스스로 실행 파일을 디스어셈블하지 않습니다.
### 그래프 예시
다음 Python 코드를 고려하세요:```python
class Auth:
def verify(self, token: str) -> bool:
return self._check_sig(token)
def _check_sig(self, token: str) -> bool:
...