wasm-tools 1.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- wasm_tools-1.0.0/LICENSE +21 -0
- wasm_tools-1.0.0/PKG-INFO +369 -0
- wasm_tools-1.0.0/README.md +342 -0
- wasm_tools-1.0.0/pyproject.toml +45 -0
- wasm_tools-1.0.0/wasm_tools/__init__.py +11 -0
- wasm_tools-1.0.0/wasm_tools/api.py +617 -0
- wasm_tools-1.0.0/wasm_tools/cli.py +104 -0
- wasm_tools-1.0.0/wasm_tools/models.py +223 -0
- wasm_tools-1.0.0/wasm_tools/opcodes.py +685 -0
- wasm_tools-1.0.0/wasm_tools/parser.py +824 -0
- wasm_tools-1.0.0/wasm_tools/visitor.py +497 -0
wasm_tools-1.0.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) Team AppThreat
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: wasm-tools
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: A pure-Python WebAssembly binary parser, disassembler, and structured analysis library
|
|
5
|
+
License: MIT
|
|
6
|
+
License-File: LICENSE
|
|
7
|
+
Keywords: wasm,binary,wabt,webassembly,security,pentesting,red teaming
|
|
8
|
+
Author: Team AppThreat
|
|
9
|
+
Author-email: cloud@appthreat.com
|
|
10
|
+
Requires-Python: >=3.10
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
16
|
+
Classifier: Intended Audience :: Developers
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Topic :: Security
|
|
19
|
+
Classifier: Topic :: Utilities
|
|
20
|
+
Provides-Extra: dev
|
|
21
|
+
Requires-Dist: pytest (>=9.0.3) ; extra == "dev"
|
|
22
|
+
Requires-Dist: pytest-cov (>=7.1.0) ; extra == "dev"
|
|
23
|
+
Project-URL: Bug Tracker, https://github.com/appthreat/wasm-tools/issues
|
|
24
|
+
Project-URL: Homepage, https://github.com/appthreat/wasm-tools
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
|
|
27
|
+
# wasm-tools
|
|
28
|
+
|
|
29
|
+
`wasm-tools` is a pure-Python WebAssembly parser and disassembler. It is designed around binary decoding and callback-based visitors rather than a large object model. The project currently focuses on practical inspection of `.wasm` binaries, objdump-style disassembly, and programmatic extraction of decoded instructions for integration into other tooling.
|
|
30
|
+
|
|
31
|
+
[](./AI-DECLARATION.md)
|
|
32
|
+
|
|
33
|
+
## What this project is for
|
|
34
|
+
|
|
35
|
+
This repository is useful when you need a lightweight WebAssembly parser that can:
|
|
36
|
+
|
|
37
|
+
- inspect a binary module without depending on native parsing libraries,
|
|
38
|
+
- produce readable instruction traces for analyst review,
|
|
39
|
+
- expose structured instruction data as Python dictionaries or JSON,
|
|
40
|
+
- behave safely on malformed or truncated input by reporting parser errors through callbacks instead of crashing the caller.
|
|
41
|
+
|
|
42
|
+
For a security engineering audience, the main value is that the code path is short and inspectable. Most behavior lives in four files:
|
|
43
|
+
|
|
44
|
+
- `wasm_tools/parser.py` for binary decoding and traversal,
|
|
45
|
+
- `wasm_tools/opcodes.py` for opcode and immediate metadata,
|
|
46
|
+
- `wasm_tools/visitor.py` for human-readable output,
|
|
47
|
+
- `wasm_tools/api.py` for library-first structured output.
|
|
48
|
+
|
|
49
|
+
## Trust and provenance
|
|
50
|
+
|
|
51
|
+
This repository includes `AI-DECLARATION.md`. That file currently states that the source code in this repository was fully generated by AI assistants, with any human edits limited to formatting or minor changes. For a technical reader, the practical implication is simple: treat the codebase as useful but high-scrutiny infrastructure. Review parser behavior, test coverage, and known gaps before depending on it in a security workflow.
|
|
52
|
+
|
|
53
|
+
The repository itself already reflects this review posture:
|
|
54
|
+
|
|
55
|
+
- parser failures are covered by unit tests for malformed input,
|
|
56
|
+
- end-to-end tests assert exact disassembly substrings,
|
|
57
|
+
- CLI and JSON outputs use module-global index spaces for functions, globals, tables, memories, and tags, including imported-entity offsets.
|
|
58
|
+
|
|
59
|
+
## Architecture
|
|
60
|
+
|
|
61
|
+
A detailed description of the WebAssembly binary format, the parser internals, visitor pattern, two-pass execution model, and security-relevant design decisions is in [ARCHITECTURE.md](./ARCHITECTURE.md).
|
|
62
|
+
|
|
63
|
+
The short version:
|
|
64
|
+
|
|
65
|
+
`BinaryReader` in `wasm_tools/parser.py` owns the binary walk. It reads the module header, iterates sections, and decodes function bodies instruction by instruction. It does not build a full AST. Instead, it emits parser events to a delegate object. The parser checks callbacks with `hasattr(...)` before calling them, so a visitor only needs to implement the hooks it cares about.
|
|
66
|
+
|
|
67
|
+
The CLI and the JSON API both run the parse twice. The first pass collects names and type information into `ObjdumpState`. The second pass uses that state to produce disassembly, section details, or a structured JSON report. The shared state lives in `wasm_tools/models.py`.
|
|
68
|
+
|
|
69
|
+
`wasm_tools/opcodes.py` defines the mapping from `(prefix, opcode)` to `(mnemonic, immediate type)`. The parser uses this table inside `BinaryReader.read_instructions()` to decide how many bytes to consume. When extending the instruction set, only this table and the immediate dispatch branches in the parser need to change.
|
|
70
|
+
|
|
71
|
+
## Relationship to the specification
|
|
72
|
+
|
|
73
|
+
The repository now includes a local specification snapshot under `specification/wasm-latest/`. The most relevant files for current implementation work are:
|
|
74
|
+
|
|
75
|
+
- `specification/wasm-latest/5.3-binary.instructions.spectec`
|
|
76
|
+
- `specification/wasm-latest/5.4-binary.modules.spectec`
|
|
77
|
+
- `specification/wasm-latest/6.3-text.instructions.spectec`
|
|
78
|
+
|
|
79
|
+
These files are useful when validating opcode encodings, section layouts, and text-to-binary expectations. The current parser is not a full implementation of everything described by the latest specification snapshot. It implements a practical subset and falls back to `unknown_<prefix>_<opcode>` names for unsupported instructions.
|
|
80
|
+
|
|
81
|
+
## Spec coverage matrix
|
|
82
|
+
|
|
83
|
+
This matrix is a planning aid, not a certification statement. It reflects what the current codebase does today based on `wasm_tools/parser.py`, `wasm_tools/opcodes.py`, `wasm_tools/visitor.py`, `wasm_tools/api.py`, and the current test suite.
|
|
84
|
+
|
|
85
|
+
Status terms used below:
|
|
86
|
+
|
|
87
|
+
- `Tested`: implemented and covered by the current automated tests.
|
|
88
|
+
- `Partial`: implemented in a limited way, or traversed without full semantic decoding.
|
|
89
|
+
- `Known gap`: explicitly tracked as missing behavior in tests.
|
|
90
|
+
- `Not implemented or unverified`: no support or no current evidence in tests.
|
|
91
|
+
|
|
92
|
+
### Module and section coverage
|
|
93
|
+
|
|
94
|
+
| Area | Spec reference | Status | Current behavior and evidence |
|
|
95
|
+
| -------------------------------------------------- | ---------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
96
|
+
| Module header and version | `5.4-binary.modules.spectec` | Tested | Validates magic and version in `BinaryReader._do_read_module()`. Error cases for short files and bad magic are covered in `tests/test_parser.py`. |
|
|
97
|
+
| Section framing and bounds checks | `5.4-binary.modules.spectec` | Tested | Reads section id and size, checks file bounds, and reports errors through `on_error`. Covered by truncated section tests. |
|
|
98
|
+
| Custom sections, generic | `5.4-binary.modules.spectec` | Partial | Parser reads custom section name and skips unknown payloads. The JSON API records the custom section name, but does not decode arbitrary custom payloads. |
|
|
99
|
+
| Custom `name` section for function and local names | `5.4-binary.modules.spectec` | Tested | Subsections 1 (function names) and 2 (local names) are decoded and stored in `ObjdumpState`. Names appear in disassembly and JSON reports. Covered by `custom_name.wasm` and `unicode_names.wat`. |
|
|
100
|
+
| Type section | `5.4-binary.modules.spectec` | Tested | Full function type decoding with GC subtype / rec-type wrappers. Params and results stored as `FuncType` in `ObjdumpState.types` and surfaced in `--details`, JSON `types[]`, and `tests/test_details.py`. |
|
|
101
|
+
| Import section | `5.4-binary.modules.spectec` | Tested | All five import kinds (func, table, memory, global, tag) fully decoded into `ImportEntry` with kind-specific fields. Exposed in `--details` output, JSON `imports[]`, and covered by `tests/test_details.py`. |
|
|
102
|
+
| Function section | `5.4-binary.modules.spectec` | Tested | Function signature indices decoded and stored via `on_function`. Used in prepass and JSON reports. |
|
|
103
|
+
| Table section | `5.4-binary.modules.spectec` | Tested | Reference type and limits decoded into `TableEntry`. Exposed in `--details` and JSON `tables[]`. |
|
|
104
|
+
| Memory section | `5.4-binary.modules.spectec` | Tested | Limits decoded (i32 and i64 variants) into `MemoryEntry`. Exposed in `--details` and JSON `memories[]`. |
|
|
105
|
+
| Global section | `5.4-binary.modules.spectec` | Tested | Value type, mutability, and constant init expression decoded into `GlobalEntry`. Exposed in `--details` and JSON `globals[]`. |
|
|
106
|
+
| Export section | `5.4-binary.modules.spectec` | Tested | All five export kinds decoded into `ExportEntry`. Exposed in `--details` and JSON `exports[]`. |
|
|
107
|
+
| Start section | `5.4-binary.modules.spectec` | Tested | Start function index stored and surfaced in JSON `start_function` field and `--details` output. |
|
|
108
|
+
| Element section | `5.4-binary.modules.spectec` | Tested | All 8 element segment variants decoded, with mode, ref type, table index, offset expression, and function index list stored in `ElementEntry`. |
|
|
109
|
+
| Code section and function bodies | `5.4-binary.modules.spectec` | Tested | Local declaration headers are consumed, instructions are decoded, and end-of-body tracking is implemented. Covered heavily by `tests/test_e2e.py` and `tests/test_json_api.py`. |
|
|
110
|
+
| Data section | `5.4-binary.modules.spectec` | Tested | Active (mem 0), passive, and active (mem x) variants decoded into `DataEntry`. Exposed in `--details` and JSON `data_segments[]`. Covered by `bulk_memory.wat` and `memory_data.wat`. |
|
|
111
|
+
| Data count section | `5.4-binary.modules.spectec` | Tested | Data count is decoded and forwarded to delegates via `on_data_count`. |
|
|
112
|
+
| Tag section | `5.4-binary.modules.spectec` | Tested | Tag entries decoded into `TagEntry` with type index. Exposed in `--details` and JSON `tags[]`. |
|
|
113
|
+
|
|
114
|
+
### Instruction coverage
|
|
115
|
+
|
|
116
|
+
| Area | Spec reference | Status | Current behavior and evidence |
|
|
117
|
+
| -------------------------------------------------------------------------------------------------- | --------------------------------- | ------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
118
|
+
| Basic parametric instructions (`unreachable`, `nop`, `drop`, `select`) | `5.3-binary.instructions.spectec` | Tested | All mapped explicitly in `OPCODES`. Typed `select` with result type vector is handled via `SELECT_T` immediate dispatch. Covered by fixture disassembly tests. |
|
|
119
|
+
| Block/control structure (`block`, `loop`, `if`, `else`, `end`) | `5.3-binary.instructions.spectec` | Tested | Block signatures and expression depth tracking are implemented in `read_instructions()`. Covered by `control_flow.wat` and `complex_flow.wat`. |
|
|
120
|
+
| Branching (`br`, `br_if`, `br_table`, `return`) | `5.3-binary.instructions.spectec` | Tested | Core branch immediates are decoded. `br_table` target list decoded and printed. Covered by `tests/test_e2e.py` and `adversarial_ops.wat`. |
|
|
121
|
+
| Direct and indirect calls (`call`, `call_indirect`) | `5.3-binary.instructions.spectec` | Tested | Direct index operands and `call_indirect` signature/table operands decoded. Covered by `call_indirect.wat` and `complex_flow.wat`. |
|
|
122
|
+
| Return-call extensions (`return_call`, `return_call_indirect`, `call_ref`, `return_call_ref`) | `5.3-binary.instructions.spectec` | Tested | All four opcodes are in `OPCODES` with correct immediate types. Covered by `tests/test_extended_ops.py`. |
|
|
123
|
+
| Variable access (`local.get/set/tee`, `global.get/set`) | `5.3-binary.instructions.spectec` | Tested | Index immediates decoded and printed. Covered by arithmetic, globals, and control-flow fixtures. |
|
|
124
|
+
| Memory load/store with memarg | `5.3-binary.instructions.spectec` | Tested | All scalar load/store instructions use the `MEMARG` decoder path. Covered by `memory_data.wat` and `complex_flow.wat`. |
|
|
125
|
+
| Integer and float constants | `5.3-binary.instructions.spectec` | Tested | `i32.const`, `i64.const`, `f32.const`, and `f64.const` immediates decoded. Edge signed immediates covered in parser tests and `adversarial_ops.wat`. |
|
|
126
|
+
| Scalar numeric arithmetic and comparisons | `5.3-binary.instructions.spectec` | Tested | Full i32, i64, f32, f64 arithmetic, comparison, and conversion opcode sets are in `OPCODES`. Sign-extension opcodes (`0xC0-0xC4`) included. Covered by `tests/test_extended_ops.py`. |
|
|
127
|
+
| Reference type instructions (`ref.null`, `ref.func`, `ref.eq`, etc.) | `5.3-binary.instructions.spectec` | Tested | `0xD0-0xD6` fully mapped. `ref.null` uses `HEAP_TYPE` immediate. `br_on_null`/`br_on_non_null` use `INDEX`. Covered by `tests/test_extended_ops.py`. |
|
|
128
|
+
| Saturating truncation (`i32.trunc_sat_*`, `i64.trunc_sat_*`) | `5.3-binary.instructions.spectec` | Tested | All eight `0xFC 0-7` opcodes in `OPCODES` with `NONE` immediate. Dispatch covered by `tests/test_extended_ops.py::test_dispatch_sat_trunc`. |
|
|
129
|
+
| Bulk memory (`memory.init`, `data.drop`, `memory.copy`, `memory.fill`) | `5.3-binary.instructions.spectec` | Tested | `0xFC 8-11` with correct binary operand order for `memory.init`. Covered by `tests/test_confidence_parser.py`, `tests/test_e2e.py`, `tests/test_json_api.py`. |
|
|
130
|
+
| Table bulk ops (`table.init`, `elem.drop`, `table.copy`, `table.grow`, `table.size`, `table.fill`) | `5.3-binary.instructions.spectec` | Tested | `0xFC 12-17` fully mapped with `TABLE_INIT`, `TABLE_COPY`, and `INDEX` immediate types. Dispatch covered by `tests/test_extended_ops.py`. |
|
|
131
|
+
| Exception handling (`throw`, `throw_ref`, `try_table`) | `5.3-binary.instructions.spectec` | Tested | `throw` (0x08), `throw_ref` (0x0A), and `try_table` (0x1F with full catch list) decoded. `TRY_TABLE_BLOCK` parses catch opcodes 0x00-0x03. Covered by `tests/test_extended_ops.py`. |
|
|
132
|
+
| GC / reference types (`0xFB` prefix, struct/array/ref ops) | `5.3-binary.instructions.spectec` | Tested | All 31 `0xFB 0-30` opcodes in `OPCODES`. `BR_ON_CAST` (flags + label + 2 heaptypes) fully decoded. `tests/test_extended_ops.py` covers table completeness and dispatch for `array.len`, `struct.new`, `ref.test`. |
|
|
133
|
+
| SIMD / vector instructions (`0xFD` prefix) | `5.3-binary.instructions.spectec` | Tested | All standard SIMD opcodes 0-275 mapped, including relaxed SIMD. Load/store use `MEMARG`, `v128.const` uses `V128_CONST` (16 raw bytes), `i8x16.shuffle` uses `V128_SHUFFLE`, lane ops use `LANE_IDX` and `MEMARG_LANE`. Covered by `tests/test_extended_ops.py`. |
|
|
134
|
+
| Threads / atomics (`0xFE` prefix) | `5.3-binary.instructions.spectec` | Tested | All atomic operations mapped. `atomic.fence` uses `ATOMIC_FENCE` (reads reserved byte). All others use `MEMARG`. Covered by `tests/test_extended_ops.py`. |
|
|
135
|
+
| Unknown opcode resilience | `5.3-binary.instructions.spectec` | Tested | Unsupported opcodes fall back to `unknown_<prefix>_<opcode>` rather than crashing. Covered by `tests/test_confidence_parser.py`. |
|
|
136
|
+
|
|
137
|
+
### Interface and analysis coverage
|
|
138
|
+
|
|
139
|
+
| Area | Status | Current behavior and evidence |
|
|
140
|
+
| --------------------------------------------- | --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
141
|
+
| CLI disassembly mode (`-d`) | Tested | Covered by `tests/test_e2e.py` with exact substring assertions across all fixture files. |
|
|
142
|
+
| CLI headers mode (`--headers`) | Tested | `BinaryReaderObjdumpHeaders` prints section id, name, size, and offset. Covered by `tests/test_details.py`. |
|
|
143
|
+
| CLI details mode (`-x`) | Tested | `BinaryReaderObjdumpDetails` prints all section contents: types, imports, exports, globals, tables, memories, data segments, elements, tags, and code bodies. Covered by `tests/test_details.py`. |
|
|
144
|
+
| JSON-friendly library API | Tested | `parse_wasm_file()` and related helpers return full semantic reports including types, imports, exports, globals, tables, memories, data segments, and elements. Covered in `tests/test_json_api.py`. |
|
|
145
|
+
| Non-throwing parse errors for library callers | Tested | Malformed inputs populate `errors` instead of forcing a traceback. Covered in parser and JSON API tests. |
|
|
146
|
+
| Full validation against the specification | Not implemented | The current code decodes and reports binary structure; it does not implement the validation chapters from the bundled specification snapshot. |
|
|
147
|
+
| Text-format parsing (`.wat` as input) | Not implemented | The repository consumes `.wat` only through the external fixture build step with `wat2wasm`. |
|
|
148
|
+
|
|
149
|
+
### How to use this matrix
|
|
150
|
+
|
|
151
|
+
The library now covers the full WebAssembly binary format at the decoding level. The remaining gaps are deliberate scope choices rather than missing work items:
|
|
152
|
+
|
|
153
|
+
1. Spec validation (type checking, structural constraints from chapters 2 and 3 of the spec) is not the goal of this library. Validation belongs in a downstream consumer such as a language runtime.
|
|
154
|
+
2. Text-format (`.wat`) input is handled externally by WABT and is not in scope.
|
|
155
|
+
3. The specification snapshot is kept locally under `specification/wasm-latest/` to serve as an authoritative reference during development but is not shipped with the distributed package.
|
|
156
|
+
|
|
157
|
+
## Command-line usage
|
|
158
|
+
|
|
159
|
+
The installed console script is `wasm-tools`, as defined in `pyproject.toml`.
|
|
160
|
+
|
|
161
|
+
Disassemble a fixture module:
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
python -m wasm_tools.cli tests/fixtures/simple_add.wasm -d
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
If installed as a package, the equivalent entrypoint is:
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
wasm-tools tests/fixtures/simple_add.wasm -d
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
Current CLI flags in `wasm_tools/cli.py`:
|
|
174
|
+
|
|
175
|
+
- `-h`, `--headers` — print section header table with ids, sizes, and offsets
|
|
176
|
+
- `-x`, `--details` — print section contents: type signatures, imports, exports, globals, tables, memories, data segments, elements, tags, and code body summaries
|
|
177
|
+
- `-d`, `--disassemble` — decode and print function body instructions
|
|
178
|
+
- `--json` — print a minified JSON report to stdout
|
|
179
|
+
- `--json-out PATH` — write a minified JSON report to `PATH`
|
|
180
|
+
- `--analysis-only` — with `--json` and/or `--json-out`, emit only the high-level `analysis` object
|
|
181
|
+
|
|
182
|
+
With no flags, `--details` is the default.
|
|
183
|
+
|
|
184
|
+
Index notes for CLI output:
|
|
185
|
+
|
|
186
|
+
- function/global/table/memory/tag indices are printed in module-global index space,
|
|
187
|
+
- locally-defined function bodies therefore start at `func[imported_function_count]` when function imports are present,
|
|
188
|
+
- section detail headers use entry counts (for example `Function[3]`, `Code[3]`, `Data[1]`) and `DataCount` prints the decoded count value.
|
|
189
|
+
|
|
190
|
+
Write a minified JSON report to a file:
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
wasm-tools tests/fixtures/simple_add.wasm --json-out simple_add.json
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
Print a minified JSON report to stdout:
|
|
197
|
+
|
|
198
|
+
```bash
|
|
199
|
+
wasm-tools tests/fixtures/simple_add.wasm --json
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
Print only the high-level analysis object to stdout:
|
|
203
|
+
|
|
204
|
+
```bash
|
|
205
|
+
wasm-tools tests/fixtures/wasi_capabilities.wasm --json --analysis-only
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
Use both JSON options together to write a file and print the same payload:
|
|
209
|
+
|
|
210
|
+
```bash
|
|
211
|
+
wasm-tools tests/fixtures/simple_add.wasm --json --json-out simple_add.json
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
Write only the analysis object to a file:
|
|
215
|
+
|
|
216
|
+
```bash
|
|
217
|
+
wasm-tools tests/fixtures/dos_growth_loop.wasm --json-out analysis.json --analysis-only
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
## Library usage
|
|
221
|
+
|
|
222
|
+
### Parse from a file
|
|
223
|
+
|
|
224
|
+
```python
|
|
225
|
+
from wasm_tools.api import parse_wasm_file
|
|
226
|
+
|
|
227
|
+
report = parse_wasm_file("tests/fixtures/simple_add.wasm")
|
|
228
|
+
print(report["module_version"])
|
|
229
|
+
print(report["function_count"])
|
|
230
|
+
print(report["functions"][0]["instructions"])
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
### Parse from bytes and emit JSON
|
|
234
|
+
|
|
235
|
+
```python
|
|
236
|
+
from wasm_tools.api import parse_wasm_bytes_json
|
|
237
|
+
|
|
238
|
+
with open("tests/fixtures/unicode_names.wasm", "rb") as wasm_file:
|
|
239
|
+
print(parse_wasm_bytes_json(wasm_file.read(), filename="unicode_names.wasm"))
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
### Report schema
|
|
243
|
+
|
|
244
|
+
The structured report currently contains:
|
|
245
|
+
|
|
246
|
+
- `file`: source path or caller-supplied label,
|
|
247
|
+
- `module_version`: wasm version from the module header, or `None` on parse failure,
|
|
248
|
+
- `section_count`: number of recorded sections,
|
|
249
|
+
- `sections`: list of section dictionaries with `index`, `id`, `name`, `size`, and `offset`,
|
|
250
|
+
- `function_count`: number of decoded function bodies,
|
|
251
|
+
- `functions`: list of function dictionaries with `index`, `name`, `signature_index`, `offset`, `body_size`, `instruction_count`, and `instructions`,
|
|
252
|
+
- `errors`: list of parsing or file read errors.
|
|
253
|
+
|
|
254
|
+
Each instruction entry contains:
|
|
255
|
+
|
|
256
|
+
- `offset`: byte offset used by the parser when the opcode was decoded,
|
|
257
|
+
- `opcode`: mnemonic from `OPCODES` or an `unknown_...` fallback,
|
|
258
|
+
- `immediates`: decoded immediate values in parser order,
|
|
259
|
+
- `decode_incomplete`: present only when a function body ended with a partially decoded instruction record.
|
|
260
|
+
|
|
261
|
+
This shape is covered by `tests/test_json_api.py`.
|
|
262
|
+
|
|
263
|
+
### High-level security analysis
|
|
264
|
+
|
|
265
|
+
The JSON report includes an `analysis` object designed for analyst triage.
|
|
266
|
+
|
|
267
|
+
- `summary`: overall `risk_score`, `risk_tier`, and `finding_count`,
|
|
268
|
+
- `capabilities`: inferred host capability tags from imports (for example `fs.path`, `network`, `process.terminate`),
|
|
269
|
+
- `profiles.memory`: memory access density, `memory.grow`, bulk-memory activity, and total data segment bytes,
|
|
270
|
+
- `profiles.control_flow`: dynamic dispatch metrics (`call_indirect`, `call_ref`) and table mutation counts,
|
|
271
|
+
- `profiles.compute`: loop depth and loop-contained memory/control-flow pressure,
|
|
272
|
+
- `findings`: actionable rule-based results with stable ids and remediation guidance.
|
|
273
|
+
|
|
274
|
+
Current built-in finding ids:
|
|
275
|
+
|
|
276
|
+
- `WASM-CAP-001`: filesystem and network host capabilities imported together.
|
|
277
|
+
- `WASM-CFG-002`: indirect call surface combined with mutable table operations.
|
|
278
|
+
- `WASM-DOS-003`: memory growth in loop context.
|
|
279
|
+
- `WASM-LOOP-004`: deep loop nesting amplification signal.
|
|
280
|
+
|
|
281
|
+
## Error handling model
|
|
282
|
+
|
|
283
|
+
The parser does not re-raise `WasmParseError` by default. `BinaryReader.read_module()` catches parse exceptions and forwards the message to `delegate.on_error(...)` when that callback exists.
|
|
284
|
+
|
|
285
|
+
This behavior is important for integration scenarios:
|
|
286
|
+
|
|
287
|
+
- command-line flows can report errors without a Python traceback,
|
|
288
|
+
- library callers can collect structured failure information,
|
|
289
|
+
- fuzzing or batch inspection pipelines can continue after a malformed file.
|
|
290
|
+
|
|
291
|
+
Unit tests cover this behavior in `tests/test_parser.py` and `tests/test_confidence_parser.py`.
|
|
292
|
+
|
|
293
|
+
Examples of currently tested failure cases include:
|
|
294
|
+
|
|
295
|
+
- truncated modules,
|
|
296
|
+
- bad magic values,
|
|
297
|
+
- sections extending beyond file boundaries,
|
|
298
|
+
- malformed LEB128 encodings,
|
|
299
|
+
- truncated instruction immediates.
|
|
300
|
+
|
|
301
|
+
## Test fixtures and what they cover
|
|
302
|
+
|
|
303
|
+
The repository uses `.wat` fixtures under `tests/fixtures/`, compiled to `.wasm` with WABT's `wat2wasm`.
|
|
304
|
+
|
|
305
|
+
Representative fixtures include:
|
|
306
|
+
|
|
307
|
+
- `simple_add.wat` for minimal arithmetic and local access,
|
|
308
|
+
- `control_flow.wat` for `block`, `loop`, `br`, and `br_if`,
|
|
309
|
+
- `memory_data.wat` for memory load semantics and data segments,
|
|
310
|
+
- `globals_imports.wat` for imported globals and functions,
|
|
311
|
+
- `call_indirect.wat` for indirect calls,
|
|
312
|
+
- `bulk_memory.wat` for `memory.init`, `data.drop`, and `memory.fill`,
|
|
313
|
+
- `complex_flow.wat` for mixed control flow, memory, direct calls, and indirect calls,
|
|
314
|
+
- `unicode_names.wat` for Unicode content,
|
|
315
|
+
- `adversarial_ops.wat` for edge immediates and `br_table`,
|
|
316
|
+
- `wasi_capabilities.wat` for host capability/risk analysis checks,
|
|
317
|
+
- `dos_growth_loop.wat` for loop + `memory.grow` DoS heuristics.
|
|
318
|
+
|
|
319
|
+
These fixtures are used in `tests/test_e2e.py` to validate the disassembly output and in `tests/test_json_api.py` to validate the structured API.
|
|
320
|
+
|
|
321
|
+
## Known limitations
|
|
322
|
+
|
|
323
|
+
The repository is a practical decoder, not a full specification implementation:
|
|
324
|
+
|
|
325
|
+
- Spec validation (type checking, module-level structural constraints) is deliberately out of scope.
|
|
326
|
+
- Text-format (`.wat`) input is handled by external WABT tooling only.
|
|
327
|
+
- The custom `name` section decodes subsections 1 (function names) and 2 (local names); other subsections such as label names are skipped.
|
|
328
|
+
- Some rarely used init-expression forms in element and data segments fall back to a hex scan rather than full expression decoding.
|
|
329
|
+
- The `analysis` layer is heuristic by design and is intended for triage, not formal proof of exploitability.
|
|
330
|
+
- The library ships with no runtime dependencies. The `specification/` directory contains only reference material and is not included in the PyPI package.
|
|
331
|
+
|
|
332
|
+
## Development workflow
|
|
333
|
+
|
|
334
|
+
Run the full test suite:
|
|
335
|
+
|
|
336
|
+
```bash
|
|
337
|
+
python -m pytest -q
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
Rebuild `.wasm` fixtures from `.wat` sources:
|
|
341
|
+
|
|
342
|
+
```bash
|
|
343
|
+
python tests/fixtures/build.py
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
The fixture build script requires WABT's `wat2wasm` binary to be available on `PATH`.
|
|
347
|
+
|
|
348
|
+
If you prefer using Poetry, the repository metadata in `pyproject.toml` indicates Poetry-based packaging:
|
|
349
|
+
|
|
350
|
+
```bash
|
|
351
|
+
poetry install
|
|
352
|
+
poetry run pytest -q
|
|
353
|
+
poetry run python tests/fixtures/build.py
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
## Guidance for reviewers and integrators
|
|
357
|
+
|
|
358
|
+
If you are evaluating this project for security tooling or pipeline integration, start with these files:
|
|
359
|
+
|
|
360
|
+
- `wasm_tools/parser.py` for parse correctness,
|
|
361
|
+
- `wasm_tools/opcodes.py` for current opcode coverage,
|
|
362
|
+
- `wasm_tools/api.py` for the stable integration surface,
|
|
363
|
+
- `tests/test_e2e.py` for output expectations,
|
|
364
|
+
- `specification/wasm-latest/5.3-binary.instructions.spectec` for spec alignment work.
|
|
365
|
+
|
|
366
|
+
## License
|
|
367
|
+
|
|
368
|
+
This project is licensed under the MIT License. See `LICENSE` for details.
|
|
369
|
+
|