forked from 1b-agent/opencadstudio-upstream
fix(automation): TCP automation requires a session token
--http (headless and GUI-hosted) and --serve --port answered anything that reached the loopback port, and the REST responses allowed every web origin. Loopback is shared by every process and user on the machine, so the port proved nothing. - Every REST request now needs Authorization: Bearer <token>, and every --serve --port request line a "token" field; otherwise 401 / unauthorized (the socket is closed). - The token is OCS_API_TOKEN when the launcher sets it, else 32 random bytes printed on stderr at startup; compared in constant time. - No CORS headers are sent, so a web page cannot read an answer. - stdio --serve and --mcp are unchanged (private to their parent / the GUI's own token). GHSA-c27p-xfx2-9r5x Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
d0bfa48037
commit
1fec34d84a
7 files changed
+195
-60
No files matched your search
+21
-11
@@ -72,15 +72,22 @@ MCP advertise the whole surface; feature detection is `capabilities` (§4).
|
||||
|
||||
`OpenCADStudio --http 8090` runs a private, window-less session behind a
|
||||
resource-oriented REST surface at `http://127.0.0.1:<port>/api/v1` (loopback
|
||||
bind only, permissive CORS, JSON bodies, one request per connection). A
|
||||
bind only, JSON bodies, one request per connection, no CORS headers). A
|
||||
machine-readable OpenAPI 3 description of the whole surface is served at
|
||||
`GET /api/v1/openapi`.
|
||||
|
||||
Every request carries `Authorization: Bearer <token>`: the value of
|
||||
`OCS_API_TOKEN` when the launcher set it, otherwise a random token printed on
|
||||
stderr at startup. A request without it is answered **401**
|
||||
`{"code":"unauthorized"}`. Loopback is shared by every process and user on
|
||||
the machine, so the port alone is no credential.
|
||||
|
||||
| Status | Meaning |
|
||||
|---|---|
|
||||
| `200` | Success (read or update) |
|
||||
| `201` | A resource was created (document, entities, block, group, selection set, layout) |
|
||||
| `400` | Validation failure — the body keeps the symbolic `code` and message |
|
||||
| `401` | Missing or wrong bearer token (`code:"unauthorized"`) |
|
||||
| `404` | Unknown route or unknown handle (`code:"entity_absent"`) |
|
||||
| `409` | State conflict (`stale_state`, `document_not_active`) or GUI required (`gui_required`) |
|
||||
| `503` | Dispatcher busy |
|
||||
@@ -126,7 +133,7 @@ Differences worth knowing:
|
||||
- Unknown POST paths are treated as op attempts and forwarded (any op of the
|
||||
surface works — including `operation` to poll a pending pick and `cancel`
|
||||
to retract it); truly unrouted paths answer `404 {"code":"unknown_route"}`.
|
||||
- `OPTIONS *` answers **204** (CORS preflight).
|
||||
- The same bearer token as §3.1 is required on every request.
|
||||
|
||||
### 3.3 stdio JSONL (`--serve`)
|
||||
|
||||
@@ -138,6 +145,9 @@ envelope; mutations use the envelope of §1).
|
||||
### 3.4 TCP (`--serve --port N`)
|
||||
|
||||
The same JSONL protocol as §3.3 over `127.0.0.1:<N>`, one client at a time.
|
||||
Every request line carries `"token":"<token>"` (the §3.1 token); a line
|
||||
without it is answered `{"ok":false,"code":"unauthorized"}` and the
|
||||
connection is closed.
|
||||
|
||||
### 3.5 AI-tool session (`--mcp`)
|
||||
|
||||
@@ -506,7 +516,7 @@ client's own log.
|
||||
|
||||
```sh
|
||||
# Create (missing layers auto-created; the batch is atomic).
|
||||
curl -s -X POST http://127.0.0.1:8090/api/v1/entities -H "Content-Type: application/json" -d '{
|
||||
curl -s -H "Authorization: Bearer $OCS_API_TOKEN" -X POST http://127.0.0.1:8090/api/v1/entities -H "Content-Type: application/json" -d '{
|
||||
"entities":[
|
||||
{"type":"Line","start":[0,0],"end":[100,0],"layer":"FRAME"},
|
||||
{"type":"Text","value":"PAGE-01","position":[10,50],"height":3.0}
|
||||
@@ -514,16 +524,16 @@ curl -s -X POST http://127.0.0.1:8090/api/v1/entities -H "Content-Type: applicat
|
||||
# → 201 {"result":{"handles":["63","64"],"created":2,"layers_created":["FRAME"]}}
|
||||
|
||||
# Filter by property.
|
||||
curl -s "http://127.0.0.1:8090/api/v1/entities?type=Line&where=%5B%7B%22path%22%3A%22%2Flayer%22%2C%22op%22%3A%22eq%22%2C%22value%22%3A%22FRAME%22%7D%5D"
|
||||
curl -s -H "Authorization: Bearer $OCS_API_TOKEN" "http://127.0.0.1:8090/api/v1/entities?type=Line&where=%5B%7B%22path%22%3A%22%2Flayer%22%2C%22op%22%3A%22eq%22%2C%22value%22%3A%22FRAME%22%7D%5D"
|
||||
|
||||
# Mark, wrap into a reusable block, plot every sheet, save as a template.
|
||||
curl -s -X PUT http://127.0.0.1:8090/api/v1/entities/64/xdata/SPEC \
|
||||
curl -s -H "Authorization: Bearer $OCS_API_TOKEN" -X PUT http://127.0.0.1:8090/api/v1/entities/64/xdata/SPEC \
|
||||
-H "Content-Type: application/json" -d '[{"code":1000,"value":"PAGE-01"}]'
|
||||
curl -s -X POST http://127.0.0.1:8090/api/v1/blocks \
|
||||
curl -s -H "Authorization: Bearer $OCS_API_TOKEN" -X POST http://127.0.0.1:8090/api/v1/blocks \
|
||||
-H "Content-Type: application/json" -d '{"name":"MARK","base":[0,0,0],"handles":["63","64"]}'
|
||||
curl -s -X POST http://127.0.0.1:8090/api/v1/plot \
|
||||
curl -s -H "Authorization: Bearer $OCS_API_TOKEN" -X POST http://127.0.0.1:8090/api/v1/plot \
|
||||
-H "Content-Type: application/json" -d '{"path":"C:/out/pages.pdf","layout":"all","per_page":true}'
|
||||
curl -s -X POST http://127.0.0.1:8090/api/v1/save \
|
||||
curl -s -H "Authorization: Bearer $OCS_API_TOKEN" -X POST http://127.0.0.1:8090/api/v1/save \
|
||||
-H "Content-Type: application/json" -d '{"path":"C:/out/session.dwt"}'
|
||||
```
|
||||
|
||||
@@ -534,14 +544,14 @@ OpenCADStudio.exe "C:/drawings/plan.dwg" --http 8090 # the editor stays intera
|
||||
|
||||
# Ask the person to pick block references; the connection parks until they
|
||||
# press Enter (or Esc). No polling loop needed.
|
||||
curl -s -m 1800 -X POST http://127.0.0.1:8090/api/v1/user_select \
|
||||
curl -s -H "Authorization: Bearer $OCS_API_TOKEN" -m 1800 -X POST http://127.0.0.1:8090/api/v1/user_select \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"request_id":"sample-1","type":"INSERT","detail":"full","prompt":"Pick the title blocks"}'
|
||||
# → {"ok":true,"status":"completed","result":{"cancelled":false,"count":2,…}}
|
||||
|
||||
# Read the same entities back, then continue driving the live drawing.
|
||||
curl -s -X POST http://127.0.0.1:8090/api/v1/get_selection -d '{}'
|
||||
curl -s -X POST http://127.0.0.1:8090/api/v1/entities/transform \
|
||||
curl -s -H "Authorization: Bearer $OCS_API_TOKEN" -X POST http://127.0.0.1:8090/api/v1/get_selection -d '{}'
|
||||
curl -s -H "Authorization: Bearer $OCS_API_TOKEN" -X POST http://127.0.0.1:8090/api/v1/entities/transform \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"handles":["6F"],"action":"move","vector":[5,0]}'
|
||||
```
|
||||
|
||||
Reference in new issue
Block a user