Skip to content

Commit 84fafe0

Browse files
committed
feat: Add ProXWire runtime implementation and LSP prototype with taint token highlighting
1 parent be6f13d commit 84fafe0

6 files changed

Lines changed: 189 additions & 0 deletions

File tree

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
ProXWire Runtime: Implementation Sketch
2+
======================================
3+
4+
This document expands the ProXWire design with more implementation details and integration points with `prox.net.shield` and the Context-Aware scheduler.
5+
6+
Dispatch
7+
--------
8+
- Intent signature: canonical string `domain:action:accepts` used as a trie key.
9+
- Router stores precompiled sanitizer & serializer pipelines keyed by intent.
10+
- Dispatch path:
11+
1. Transport receives bytes -> wraps as `Tainted[Bytes]` with metadata.
12+
2. `prox.net.shield.auto_sanitize` runs pipeline for intent; returns `Sanitized[T]`.
13+
3. Dispatcher schedules handler with `Context`.
14+
15+
Scheduler integration
16+
---------------------
17+
- Handlers are scheduled with `Context` describing `intent`, `priority_hint`, `ttl`, and `taint_level`.
18+
- Fast lanes: runtime maintains dedicated worker pools per intent priority.
19+
20+
Optimizations
21+
-------------
22+
- Precompile sanitizer pipeline into an efficient C++ fast-path for common schemas.
23+
- Inline intent matching where header `X-Intent` exists, fallback to content-based inference.
24+
25+
Example (lowering sketch)
26+
-------------------------
27+
// High-level route
28+
@intent(domain:"user", action:"create", accepts:"application/json")
29+
async fn create_user(req: Sanitized[UserCreate], ctx: Context) -> JSON { ... }
30+
31+
// Compiler output (sketch):
32+
// 1) Register intent signature and sanitizer at module init
33+
prox.net.shield.register_sanitizer("user:create:application/json", compiled_pipeline)
34+
ProXWire.Router.register_handler("user:create:application/json", &create_user_wrapper)
35+
36+
// 2) Wrapper ensures typed call
37+
fn create_user_wrapper(raw_req, ctx) {
38+
let sanitized = prox.net.shield.auto_sanitize(raw_req.tainted, "user:create:application/json")
39+
if sanitized.is_err() { return Response(400) }
40+
return create_user(sanitized.unwrap(), ctx)
41+
}

libs/proxwire.prox

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// ProXWire runtime skeleton (pseudo-ProXPL)
2+
module ProXWire {
3+
4+
type Intent = { domain: Str, action: Str, accepts: Str, produces: Str }
5+
6+
// Router compiles an intent trie for fast dispatch
7+
struct Router {
8+
// internal: map from intent signature -> handler
9+
handlers: Map[Str, fn(Sanitized[Bytes], Context) -> Response]
10+
11+
fn add_intent(handler_fn) {
12+
// compiler/runtime introspects handler to extract `@intent` metadata
13+
sig = handler_fn.intent_signature
14+
handlers.insert(sig, handler_fn)
15+
}
16+
17+
fn dispatch(req: Request, ctx: Context) -> Response {
18+
// Determine intent from request (headers / route hints)
19+
sig = infer_intent(req)
20+
let handler = handlers.get(sig)
21+
if handler == None {
22+
return Response(404)
23+
}
24+
// Shield sanitization: auto-sanitize before calling handler
25+
let sanitized = prox.net.shield.auto_sanitize(req.body.tainted, sig)
26+
if sanitized.is_err() {
27+
return Response(400, body: { error: sanitized.err })
28+
}
29+
// schedule as async task with Context
30+
return spawn_task(handler, sanitized.unwrap(), ctx)
31+
}
32+
}
33+
34+
// Server harness (simplified)
35+
struct Server {
36+
router: Router
37+
38+
fn new(router: Router) -> Server { Server{router} }
39+
40+
fn listen(addr: Str, port: Int) {
41+
// transport loop (HTTP/TCP) receives `Request` objects
42+
for req in Transport.accept(addr, port) {
43+
// build context from request metadata
44+
ctx = Context(intent: infer_intent(req), priority_hint: 0, ttl: 30s, taint_level: 1.0)
45+
// dispatch (non-blocking)
46+
_ = router.dispatch(req, ctx)
47+
}
48+
}
49+
}
50+
51+
// Example helper: spawn async task using runtime scheduler
52+
fn spawn_task(handler, sanitized, ctx) -> Response {
53+
// runtime: create task descriptor with Context fields
54+
task = Task.create(handler, sanitized, ctx)
55+
Scheduler.enqueue(task)
56+
return Response(202) // accepted; real response may be awaited
57+
}
58+
59+
}

lsp/README.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
ProXPL LSP Prototype
2+
=====================
3+
4+
This folder contains a minimal LSP prototype that highlights identifiers prefixed with `taint_` as tainted tokens.
5+
6+
Setup
7+
-----
8+
Install dependencies in a virtualenv:
9+
10+
```powershell
11+
python -m venv .venv
12+
.\.venv\Scripts\Activate.ps1
13+
pip install -r requirements.txt
14+
```
15+
16+
Run
17+
---
18+
19+
```powershell
20+
python proxpl_lsp_taint.py
21+
```
22+
23+
Note: This is a minimal prototype for demonstration. Integrating with editors requires providing the appropriate semantic token legend and mapping token types on the client.

lsp/proxpl_lsp_taint.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
"""
2+
Minimal ProXPL LSP prototype that highlights tokens prefixed with 'taint_' as `tainted`.
3+
This uses `pygls` as a demonstration. Install with `pip install -r requirements.txt`.
4+
"""
5+
from pygls.server import LanguageServer
6+
from pygls.lsp.types import (DidOpenTextDocumentParams, DidChangeTextDocumentParams,
7+
InitializeParams, TextDocumentItem, Position,
8+
Range, SemanticTokens, SemanticTokensLegend,
9+
SemanticTokensParams)
10+
from pygls.lsp.types import SemanticTokens, SemanticTokensParams
11+
12+
import re
13+
14+
SERVER = LanguageServer('proxpl-lsp', 'v0.1')
15+
16+
TAINT_TOKEN_TYPE = 100 # custom type index (client must map)
17+
18+
@SERVER.feature('textDocument/didOpen')
19+
def did_open(ls: LanguageServer, params: DidOpenTextDocumentParams):
20+
doc = params.text_document
21+
publish_taint_tokens(ls, doc.text, doc.uri)
22+
23+
@SERVER.feature('textDocument/didChange')
24+
def did_change(ls: LanguageServer, params: DidChangeTextDocumentParams):
25+
doc = ls.workspace.get_document(params.text_document.uri)
26+
publish_taint_tokens(ls, doc.source, doc.uri)
27+
28+
def publish_taint_tokens(ls: LanguageServer, text: str, uri: str):
29+
tokens = [] # as list of integers per LSP: [line, startChar, length, tokenType, tokenModifiers]
30+
for lineno, line in enumerate(text.splitlines()):
31+
for m in re.finditer(r"\btaint_[A-Za-z0-9_]+\b", line):
32+
start = m.start()
33+
length = m.end() - m.start()
34+
tokens.extend([lineno, start, length, TAINT_TOKEN_TYPE, 0])
35+
st = SemanticTokens(data=tokens)
36+
# Semantic token publishing uses a custom notification for simplicity
37+
ls.show_message_log(f"Publishing {len(tokens)//5} taint tokens for {uri}")
38+
ls.send_notification('textDocument/semanticTokens', {'uri': uri, 'tokens': tokens})
39+
40+
def main():
41+
import argparse
42+
parser = argparse.ArgumentParser()
43+
parser.add_argument('--tcp', action='store_true', help='Run LSP server over stdio (default)')
44+
args = parser.parse_args()
45+
SERVER.start_io()
46+
47+
if __name__ == '__main__':
48+
main()

lsp/requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pygls>=0.13.0

tools/build_asr_demo_vs.bat

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
@echo off
2+
REM build_asr_demo_vs.bat
3+
REM Compile the ASR demo using MSVC `cl` (Developer Command Prompt)
4+
5+
IF "%1"=="clean" GOTO :clean
6+
7+
IF NOT EXIST build (
8+
mkdir build
9+
)
10+
11+
echo Compiling with MSVC cl...
12+
cl /EHsc /O2 runtime\asr_lockfree_queue.cpp runtime\asr_supervisor.cpp tests\asr_queue_test.cpp /Fe:build\asr_test.exe
13+
exit /b %errorlevel%
14+
15+
:clean
16+
del build\asr_test.exe
17+
exit /b 0

0 commit comments

Comments
 (0)