@terpjs/spec 0.21.0 → 0.22.0
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.
- package/VERSION +1 -1
- package/catalog/backend/declared_read_only_routes_do_not_write.json +26 -0
- package/corpus/backend/declared_read_only_routes_do_not_write/compliant-01/modules/notes/router.py +4 -0
- package/corpus/backend/declared_read_only_routes_do_not_write/compliant-02/modules/notes/router.py +8 -0
- package/corpus/backend/declared_read_only_routes_do_not_write/violation-01/expected-findings.json +7 -0
- package/corpus/backend/declared_read_only_routes_do_not_write/violation-01/modules/notes/router.py +4 -0
- package/corpus/backend/declared_read_only_routes_do_not_write/violation-02/expected-findings.json +7 -0
- package/corpus/backend/declared_read_only_routes_do_not_write/violation-02/modules/notes/router.py +5 -0
- package/package.json +1 -1
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.
|
|
1
|
+
0.22.0
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "backend/declared_read_only_routes_do_not_write",
|
|
3
|
+
"surface": "backend",
|
|
4
|
+
"title": "A route that declares itself read-only must not mutate",
|
|
5
|
+
"intent": "Write authority is derived from the HTTP method, which is right for almost every route and blind to one: the handler that uses an unsafe method because its input is a body, not because it writes — validating a candidate document, previewing an import, costing a plan. Undeclared, such a route is pure only by the absence of a write: a guarantee made of missing code, which holds until an edit adds a line and which no rule and no reviewer is prompted to check. Declaring the intent makes it enforceable in both layers: the build-time rule refuses a declared handler that calls a mutating service method, and the runtime binder marks the request read-only so the write chokepoint refuses a write the rule cannot see statically (through a helper, a subscriber, a capability). Fix a violation by removing the write — putting it behind its own route — or by removing the declaration, whichever the route was meant to be; never both, because a declared handler that writes leaves the platform holding two answers to the same promise. Authorization is deliberately unchanged: a declared route is still authorized at the write tier, because declaring purity narrows what the handler may do, never what the caller must hold.",
|
|
6
|
+
"layer": "static-bespoke",
|
|
7
|
+
"enforcement": [
|
|
8
|
+
{
|
|
9
|
+
"kind": "build-time",
|
|
10
|
+
"tool": "terp.arch",
|
|
11
|
+
"ref": "check_declared_read_only_routes_do_not_write"
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"kind": "runtime",
|
|
15
|
+
"tool": "terp.core",
|
|
16
|
+
"ref": "build_read_only_request_binder"
|
|
17
|
+
}
|
|
18
|
+
],
|
|
19
|
+
"opt_out": "# arch-allow-declared-read-only-routes-do-not-write: <reason>",
|
|
20
|
+
"runtime": {
|
|
21
|
+
"applicability": "required"
|
|
22
|
+
},
|
|
23
|
+
"reference": "Mutating BaseService calls (create/update/delete/_save/_remove) inside a handler decorated with terp.core.read_only are flagged; create_app binds a declared route's request read-only (build_read_only_request_binder).",
|
|
24
|
+
"guide_topic": "module",
|
|
25
|
+
"corpus": true
|
|
26
|
+
}
|
package/corpus/backend/declared_read_only_routes_do_not_write/compliant-02/modules/notes/router.py
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
@router.post('/preview', response_model=NotePreview)
|
|
2
|
+
@read_only
|
|
3
|
+
def preview_import(payload) -> NotePreview:
|
|
4
|
+
existing = service.list(skip=0, limit=10)
|
|
5
|
+
return NotePreview(rows=payload.rows, existing=existing)
|
|
6
|
+
@router.post('/', response_model=NoteRead)
|
|
7
|
+
def create_note(payload) -> NoteRead:
|
|
8
|
+
return service.create(payload)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@terpjs/spec",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.22.0",
|
|
4
4
|
"description": "The Terp Standard — stack-neutral rule catalog, violation corpus, finding format, and refused-surface declaration (ADRs 0080/0081; packaged per ADR 0082, published per ADR 0086). Data only: consumers resolve the spec root via require.resolve('@terpjs/spec/package.json').",
|
|
5
5
|
"files": [
|
|
6
6
|
"VERSION",
|