Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 25 additions & 9 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,29 +18,29 @@ jobs:
name: Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false

- uses: astral-sh/ruff-action@0ce1b0bf8b818ef400413f810f8a11cdbda0034b # v4.0.0
- uses: astral-sh/ruff-action@278981a28ce3188b1e39527901f38254bf3aac89 # v4.1.0
with:
args: check src/ tests/

- uses: astral-sh/ruff-action@0ce1b0bf8b818ef400413f810f8a11cdbda0034b # v4.0.0
- uses: astral-sh/ruff-action@278981a28ce3188b1e39527901f38254bf3aac89 # v4.1.0
with:
args: format --check src/ tests/

type-check:
name: Type Check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false

- uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0
- uses: astral-sh/setup-uv@f98e06938123ccabd21905ea5d0069192241f9f1 # v8.3.1

- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
- uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
with:
python-version: "3.13"

Expand All @@ -55,16 +55,32 @@ jobs:
matrix:
python-version: ["3.12", "3.13", "3.14"]
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false

- uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0
- uses: astral-sh/setup-uv@f98e06938123ccabd21905ea5d0069192241f9f1 # v8.3.1

- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
- uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
with:
python-version: ${{ matrix.python-version }}

- run: uv sync --all-groups

- run: uv run pytest -q tests/

package:
name: Package Smoke Test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false

- uses: astral-sh/setup-uv@f98e06938123ccabd21905ea5d0069192241f9f1 # v8.3.1

- run: uv build
- run: uv venv /tmp/trailmark-wheel && uv pip install --python /tmp/trailmark-wheel dist/*.whl
- run: /tmp/trailmark-wheel/bin/python tests/package_smoke.py tests/fixtures/kat
- run: uv venv /tmp/trailmark-sdist && uv pip install --python /tmp/trailmark-sdist dist/*.tar.gz
- run: /tmp/trailmark-sdist/bin/python tests/package_smoke.py tests/fixtures/kat
6 changes: 3 additions & 3 deletions .github/workflows/mutation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,13 @@ jobs:
name: Mutation Testing
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false

- uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0
- uses: astral-sh/setup-uv@f98e06938123ccabd21905ea5d0069192241f9f1 # v8.3.1

- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
- uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
with:
python-version: "3.13"

Expand Down
12 changes: 8 additions & 4 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,24 @@ jobs:
permissions:
contents: read
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false

- uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0
- uses: astral-sh/setup-uv@f98e06938123ccabd21905ea5d0069192241f9f1 # v8.3.1
with:
enable-cache: false

- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
- uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
with:
python-version: "3.13"

- run: uv build

- run: uv venv /tmp/trailmark-release && uv pip install --python /tmp/trailmark-release dist/*.whl

- run: /tmp/trailmark-release/bin/python tests/package_smoke.py tests/fixtures/kat

- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: dist
Expand All @@ -47,7 +51,7 @@ jobs:
name: dist
path: dist/

- uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # v4.1.0
- uses: actions/attest-build-provenance@0f67c3f4856b2e3261c31976d6725780e5e4c373 # v4.1.1
with:
subject-path: "dist/*"

Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/semgrep.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@ jobs:
name: Semgrep Scan
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false

- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
- uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
with:
python-version: "3.13"

Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/zizmor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@ jobs:
name: Workflow Security Audit
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false

- uses: zizmorcore/zizmor-action@5f14fd08f7cf1cb1609c1e344975f152c7ee938d # v0.5.6
- uses: zizmorcore/zizmor-action@192e21d79ab29983730a13d1382995c2307fbcaa # v0.5.7
with:
version: latest
advanced-security: false
21 changes: 21 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Changelog

## 0.5.0

- Pin `tree-sitter` to the compatible 0.25 series, with large-file native-crash
regressions for Go, Rust, Solidity, and TypeScript (#61, #62).
- Detect Solidity entrypoints from parser metadata, exclude interfaces, suppress
overridden base implementations, and expose visibility/mutability (#57).
- Resolve straightforward constructed TypeScript interface receivers and
document call-reachability versus taint limitations (#30).
- Add PostgreSQL-oriented SQL schema, table, view, function, procedure, and
dependency extraction (#59).
- Add stable `.trailmark/links.toml` configuration for cross-language, FFI, RPC,
and external/binary graph links (#58).
- Support C# file-scoped namespaces (#63).
- Document grammar caching and TLS-inspection/offline installation (#39).
- Add wheel/sdist installed-package smoke tests across all supported languages.

New `NodeKind` members are additive. `.trailmark/links.toml` is a new stable
configuration interface. Dynamic dispatch, full SQL query semantics, and true
interprocedural taint analysis remain out of scope for this release.
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## Setup

Requires Python >= 3.13 and [uv](https://docs.astral.sh/uv/).
Requires Python >= 3.12 and [uv](https://docs.astral.sh/uv/).

```bash
uv sync --all-groups
Expand Down
60 changes: 59 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ A language-specific parser walks the directory, parses each file into a tree-sit
| Proto | `.proto` | services, RPCs, messages, fields, enums |
| Thrift | `.thrift` | services, functions, structs, fields, enums |
| GraphQL | `.graphql`, `.gql` | object types, root operations, fields, enums |
| SQL | `.sql` | schemas, tables, views, functions, procedures |

```mermaid
flowchart TD
Expand Down Expand Up @@ -117,7 +118,7 @@ The `QueryEngine` provides a high-level API over the indexed graph:
| `paths_between(src, dst)` | All simple call paths between two nodes |
| `connect_subgraphs(source, target)` | Paths connecting two named subgraphs |
| `entrypoint_paths_to(name)` | Paths from any detected entrypoint to the target |
| `attack_surface()` | Entrypoints tagged with trust level and asset value |
| `attack_surface()` | Entrypoints tagged with trust level, asset value, and parser attributes when present |
| `complexity_hotspots(n)` | Functions with cyclomatic complexity ≥ n |
| `functions_that_raise(exc)` | Functions whose parser-detected exception list includes `exc` |
| `generic_parameters(name)` | Generic type parameters declared by a node |
Expand Down Expand Up @@ -261,6 +262,14 @@ uv sync --all-groups

Requires Python ≥ 3.12.

Trailmark uses `tree-sitter-language-pack` for most grammars. Current releases
use the platform certificate store for grammar downloads. In TLS-inspected or
offline environments, pre-populate the package cache with
`python -c "import tree_sitter_language_pack as p; p.download_all()"` on a
matching platform, then copy the resulting `tree-sitter-language-pack` cache
directory to the target machine. `HTTPS_PROXY` is also honored. The SQL grammar
ships as the `tree-sitter-sql` wheel dependency and does not use that cache.

## Usage

```bash
Expand Down Expand Up @@ -392,6 +401,52 @@ Later entries override earlier ones when two rules tag the same node, so place b

See [docs/entrypoint-patterns.md](docs/entrypoint-patterns.md) for the full reference, including frameworks not yet implemented (Express / Koa / Fastify, Laravel, Cobra, axum, warp, clap, and others) with grep-ready patterns contributors can use to add new detectors.

Solidity detection uses parser metadata rather than signature regexes. Interface
declarations are excluded and a derived override suppresses the matching base
implementation. Concrete `public` and `external` functions remain entrypoints,
including `view` and `pure` functions; their `solidity_visibility` and
`solidity_mutability` attributes are returned by `attack_surface()` so callers
can distinguish read-only exposure. `attack_surface()` includes parser-specific
entrypoint attributes when they are attached to the underlying graph node.

### Cross-language and external links

Polyglot parsing merges language graphs, but many RPC, FFI, subprocess, and
host/contract relationships are not visible in source syntax. Declare these
deterministically in `.trailmark/links.toml`:

```toml
[[link]]
source = "backend:submit"
target = "contract:Verifier.verify"
kind = "calls" # defaults to calls
confidence = "certain" # defaults to inferred
description = "JSON-RPC eth_call"

[[link]]
source = "backend:notify"
target = "payments-webhook"
external = true # required when either endpoint is unresolved
```

References may be exact node IDs or unique names/suffixes. Ambiguous references,
unknown internal endpoints, invalid enum values, and malformed TOML raise
`ValueError`. Setting `external = true` explicitly permits unresolved endpoints
and creates proxy nodes. This file is a stable public configuration interface.

### Analysis limitations

- `entrypoint_paths_to()` reports call-graph reachability, not attacker-controlled
data flow. Use preanalysis taint results as a coarse separate signal; Trailmark
does not yet perform interprocedural taint analysis.
- TypeScript resolves direct calls and straightforward receivers assigned with
`new ConcreteClass()`. Interface dispatch through manifests, computed property
names, dependency-injection containers, and other dynamic mechanisms remains
best-effort.
- SQL support is PostgreSQL-oriented and extracts schemas, tables, views,
functions, procedures, and routine/view dependencies. It is not a complete
SQL dialect validator or query-semantic analyzer.

### Programmatic API

```python
Expand Down Expand Up @@ -456,6 +511,9 @@ engine.augment_sarif("results.sarif")
engine.augment_weaudit("findings.json")
```

`NodeKind.SCHEMA`, `TABLE`, `VIEW`, and `PROCEDURE` are additive in v0.5.0;
consumers that exhaustively match enum values should add cases for them.

## Development

```bash
Expand Down
5 changes: 3 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "hatchling.build"

[project]
name = "trailmark"
version = "0.4.0"
version = "0.5.0"
description = "Parse source code into a queryable graph of functions, classes, calls, and semantic annotations"
readme = "README.md"
requires-python = ">=3.12"
Expand All @@ -14,7 +14,8 @@ authors = [
]
dependencies = [
"tree-sitter~=0.25.0",
"tree-sitter-language-pack>=1.8,<2.0",
"tree-sitter-language-pack>=1.9,<2.0",
"tree-sitter-sql>=0.3.11,<0.4",
"rustworkx>=0.17,<1.0",
]

Expand Down
2 changes: 1 addition & 1 deletion src/trailmark/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@
"parse_file",
"supported_languages",
]
__version__ = "0.4.0"
__version__ = "0.5.0"
Loading
Loading