tree-sitter: fit script constants and attributes into existing feature set
- Dominant language
- Python
- Stars
- 6.2k
- Forks
- 726
- Avg merge
- 11d 11h
- Merged PRs (30d)
- 7
Description
Imported constants and environment/module attributes are currently extracted as `API` features formatted with `::` (e.g., `api(socket::AF_INET)`, `api(os::environ)`, `api(win32con::FILE_ATTRIBUTE_HIDDEN)`).
This causes a few issues for rule authoring:
1. **Misleading feature semantics**: `socket.AF_INET` and `os.environ` are not callable routines/APIs. Treating them as `api` confuses rule authors.
2. **Lost rule reuse**: Existing rules for native and .NET binaries already match on `number: 2 = AF_INET` or `property/read: ...`. Having script-specific `api(socket::AF_INET)` prevents existing rules from matching scripts without adding duplicate statements.
3. **No read vs. write distinction**: Module attributes like `os.environ` can be read or modified, but cannot match `property/read` or `property/write`.
---
## Proposed Solution
Fit script constants and attributes into capa's existing feature set without extending rule syntax:
### 1. Numeric & Enum Constants $\rightarrow$ `Number`
Map known enum/flag constants in signature files (`py.json`, `cs.json`, etc.) to their numeric values and emit them as `Number` with descriptions:
- `socket.AF_INET` $\rightarrow$ `number(0x2 = AF_INET)`
- `socket.SOCK_STREAM` $\rightarrow$ `number(0x1 = SOCK_STREAM)`
- `win32con.FILE_ATTRIBUTE_HIDDEN` $\rightarrow$ `number(0x2 = FILE_ATTRIBUTE_HIDDEN)`
- `subprocess.PIPE` $\rightarrow$ `number(-0x1 = PIPE)`
**Rule Example**:
Existing rules written for compiled binaries immediately match scripts:
```yaml
- and:
- api: socket.socket
- number: 2 = AF_INET
- number: 1 = SOCK_STREAM
```
### 2. Object & Environment Attributes $\rightarrow$ `Property`
Emit module/object attributes (like `os.environ`, `sys.executable`, `process.env`, `Process.StartInfo.FileName`) as `Property` using the standard `Scope::Member` convention (`os::environ`).
Use AST parent inspection to detect access mode:
- **`property/write`**: Property is the target (`left` child) of an assignment (e.g., `os.environ["appdata"] = "..."`, `p.StartInfo.FileName = "..."`).
- **`property/read`**: Property is read in an expression, condition, or function argument.
- **`property`**: Generic match for either access type.
**Rule Example**:
```yaml
- property/read: os::environ
- property/write: os::environ
```
---
## Benefits
- **Zero rule syntax changes**: Reuses existing `number`, `property`, `property/read`, and `property/write` keywords.
- **Consistent across languages**: Scales cleanly across Python, C#, PowerShell (`$env:APPDATA`), JavaScript (`process.env`), and native binaries.
- **Maximized rule reuse**: Rules looking for standard OS numbers/flags work across both compiled binaries and scripts out of the box.
Contributor guide
Assessment
This issue has not been assessed yet.