@lovrozagar/oat 0.12.14 → 0.12.16
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/dist/conformance/suite.d.ts.map +1 -1
- package/dist/conformance/suite.js +2 -0
- package/dist/conformance/suite.js.map +1 -1
- package/dist/reference/defects.d.ts +4 -0
- package/dist/reference/defects.d.ts.map +1 -1
- package/dist/reference/defects.js +4 -0
- package/dist/reference/defects.js.map +1 -1
- package/dist/reference/http.d.ts.map +1 -1
- package/dist/reference/http.js +27 -3
- package/dist/reference/http.js.map +1 -1
- package/dist/reference/spec.d.ts.map +1 -1
- package/dist/reference/spec.js +81 -1
- package/dist/reference/spec.js.map +1 -1
- package/dist/report/console.d.ts.map +1 -1
- package/dist/report/console.js +2 -1
- package/dist/report/console.js.map +1 -1
- package/dist/runtime/check-costs.d.ts.map +1 -1
- package/dist/runtime/check-costs.js +2 -0
- package/dist/runtime/check-costs.js.map +1 -1
- package/dist/runtime/check-ids.d.ts +1 -1
- package/dist/runtime/check-ids.d.ts.map +1 -1
- package/dist/runtime/check-ids.js +3 -0
- package/dist/runtime/check-ids.js.map +1 -1
- package/dist/runtime/checks.d.ts.map +1 -1
- package/dist/runtime/checks.js +4 -0
- package/dist/runtime/checks.js.map +1 -1
- package/dist/runtime/disposable.d.ts +28 -0
- package/dist/runtime/disposable.d.ts.map +1 -0
- package/dist/runtime/disposable.js +149 -0
- package/dist/runtime/disposable.js.map +1 -0
- package/dist/runtime/root-read.d.ts +20 -0
- package/dist/runtime/root-read.d.ts.map +1 -0
- package/dist/runtime/root-read.js +66 -0
- package/dist/runtime/root-read.js.map +1 -0
- package/dist/runtime/run.d.ts.map +1 -1
- package/dist/runtime/run.js +39 -1
- package/dist/runtime/run.js.map +1 -1
- package/dist/runtime/scope.d.ts.map +1 -1
- package/dist/runtime/scope.js +3 -0
- package/dist/runtime/scope.js.map +1 -1
- package/dist/runtime/self.d.ts +4 -2
- package/dist/runtime/self.d.ts.map +1 -1
- package/dist/runtime/self.js +53 -6
- package/dist/runtime/self.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `x-destructive` runs as a second user registered through the spec, never the suite caller.
|
|
3
|
+
* Afterwards the suite caller's own read still returns 200. When the operation invalidates
|
|
4
|
+
* that read, the disposable credential's next read is 401.
|
|
5
|
+
*/
|
|
6
|
+
import type { Check } from "./checks.ts";
|
|
7
|
+
import type { Client } from "./client.ts";
|
|
8
|
+
import { type FindingCollector } from "./finding.ts";
|
|
9
|
+
import type { OperationModel, SpecModel } from "../spec/graph.ts";
|
|
10
|
+
import type { GradeLedger } from "./scope.ts";
|
|
11
|
+
import type { Hooks } from "../config/define-config.ts";
|
|
12
|
+
export declare const DISPOSABLE_CHECK_ID: "auth.destructive-spares-the-caller";
|
|
13
|
+
export declare function destructiveOps(model: SpecModel): OperationModel[];
|
|
14
|
+
export declare function runDisposableOps(input: {
|
|
15
|
+
auth: () => Record<string, string>;
|
|
16
|
+
client: Client;
|
|
17
|
+
findings: FindingCollector;
|
|
18
|
+
checksRun?: Set<string>;
|
|
19
|
+
grades: GradeLedger;
|
|
20
|
+
inScope: (operationId: string) => boolean;
|
|
21
|
+
mode: "full" | "targeted";
|
|
22
|
+
model: SpecModel;
|
|
23
|
+
nonce?: string;
|
|
24
|
+
resolveInput?: Hooks["resolveInput"];
|
|
25
|
+
seed?: number;
|
|
26
|
+
}): Promise<void>;
|
|
27
|
+
export declare const disposableCheck: Check;
|
|
28
|
+
//# sourceMappingURL=disposable.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"disposable.d.ts","sourceRoot":"","sources":["../../src/runtime/disposable.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AACxC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACzC,OAAO,EAAY,KAAK,gBAAgB,EAAgB,MAAM,cAAc,CAAA;AAE5E,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AACjE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAI7C,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,4BAA4B,CAAA;AAEvD,eAAO,MAAM,mBAAmB,sCAAkE,CAAA;AAIlG,wBAAgB,cAAc,CAAC,KAAK,EAAE,SAAS,GAAG,cAAc,EAAE,CAEjE;AA+BD,wBAAsB,gBAAgB,CAAC,KAAK,EAAE;IAC7C,IAAI,EAAE,MAAM,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAClC,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,gBAAgB,CAAA;IAC1B,SAAS,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;IACvB,MAAM,EAAE,WAAW,CAAA;IACnB,OAAO,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,OAAO,CAAA;IACzC,IAAI,EAAE,MAAM,GAAG,UAAU,CAAA;IACzB,KAAK,EAAE,SAAS,CAAA;IAChB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,YAAY,CAAC,EAAE,KAAK,CAAC,cAAc,CAAC,CAAA;IACpC,IAAI,CAAC,EAAE,MAAM,CAAA;CACb,GAAG,OAAO,CAAC,IAAI,CAAC,CA8GhB;AAcD,eAAO,MAAM,eAAe,EAAE,KAQ7B,CAAA"}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `x-destructive` runs as a second user registered through the spec, never the suite caller.
|
|
3
|
+
* Afterwards the suite caller's own read still returns 200. When the operation invalidates
|
|
4
|
+
* that read, the disposable credential's next read is 401.
|
|
5
|
+
*/
|
|
6
|
+
import { ASSERTED } from "./finding.js";
|
|
7
|
+
import { buildCohort, isOverflowError } from "./fixture.js";
|
|
8
|
+
import { applyResolveInput } from "./input.js";
|
|
9
|
+
import { requestSchemaOf } from "./world.js";
|
|
10
|
+
export const DISPOSABLE_CHECK_ID = "auth.destructive-spares-the-caller";
|
|
11
|
+
const REGISTER_LIKE = /register|sign[-_]?up/i;
|
|
12
|
+
export function destructiveOps(model) {
|
|
13
|
+
return model.operations.filter((op) => op.destructive);
|
|
14
|
+
}
|
|
15
|
+
function registerOperation(model) {
|
|
16
|
+
return model.operations.find((op) => op.freshPrincipal || REGISTER_LIKE.test(op.operationId) || REGISTER_LIKE.test(op.path));
|
|
17
|
+
}
|
|
18
|
+
function callerRead(model) {
|
|
19
|
+
return model.operations.find((op) => op.identity === "self" && op.action === "read" && op.pathParams.length === 0);
|
|
20
|
+
}
|
|
21
|
+
function requiredSchema(schema) {
|
|
22
|
+
const required = Array.isArray(schema.required)
|
|
23
|
+
? schema.required.filter((name) => typeof name === "string")
|
|
24
|
+
: [];
|
|
25
|
+
if (required.length === 0)
|
|
26
|
+
return schema;
|
|
27
|
+
const properties = schema.properties;
|
|
28
|
+
if (properties === null || typeof properties !== "object" || Array.isArray(properties))
|
|
29
|
+
return schema;
|
|
30
|
+
const source = properties;
|
|
31
|
+
const picked = {};
|
|
32
|
+
for (const name of required)
|
|
33
|
+
if (name in source)
|
|
34
|
+
picked[name] = source[name];
|
|
35
|
+
return { ...schema, properties: picked, required };
|
|
36
|
+
}
|
|
37
|
+
function tokenOf(body) {
|
|
38
|
+
if (body === null || typeof body !== "object")
|
|
39
|
+
return null;
|
|
40
|
+
const token = body.access_token;
|
|
41
|
+
return typeof token === "string" && token !== "" ? token : null;
|
|
42
|
+
}
|
|
43
|
+
export async function runDisposableOps(input) {
|
|
44
|
+
const ops = destructiveOps(input.model).filter((op) => input.mode === "full" || input.inScope(op.operationId));
|
|
45
|
+
if (ops.length === 0)
|
|
46
|
+
return;
|
|
47
|
+
input.checksRun?.add(DISPOSABLE_CHECK_ID);
|
|
48
|
+
const ids = ops.map((op) => op.operationId);
|
|
49
|
+
const register = registerOperation(input.model);
|
|
50
|
+
const read = callerRead(input.model);
|
|
51
|
+
if (register === undefined || read === undefined) {
|
|
52
|
+
const reason = register === undefined ? "no register operation" : "no caller read";
|
|
53
|
+
input.grades.skipped(ids, reason);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
const schema = requestSchemaOf(register, input.model);
|
|
57
|
+
let body = {};
|
|
58
|
+
if (schema !== null) {
|
|
59
|
+
try {
|
|
60
|
+
const [member] = buildCohort(requiredSchema(schema), input.seed ?? 1, ["baseline"], register.operationId, input.nonce === undefined ? {} : { nonce: input.nonce });
|
|
61
|
+
body = member?.body ?? {};
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
if (!isOverflowError(error))
|
|
65
|
+
throw error;
|
|
66
|
+
input.grades.skipped(ids, error instanceof Error ? error.message : "register body could not be generated");
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
if (input.resolveInput !== undefined) {
|
|
70
|
+
body = await applyResolveInput(body, register.operationId, schema, input.resolveInput);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
const signed = await input.client.request(register.method, register.path, {
|
|
74
|
+
body,
|
|
75
|
+
headers: {},
|
|
76
|
+
operationId: register.operationId,
|
|
77
|
+
});
|
|
78
|
+
const token = tokenOf(signed.responseBody);
|
|
79
|
+
const issued = signed.responseBody !== null && typeof signed.responseBody === "object"
|
|
80
|
+
? signed.responseBody
|
|
81
|
+
: {};
|
|
82
|
+
if (signed.status < 200 || signed.status >= 300 || token === null) {
|
|
83
|
+
input.findings
|
|
84
|
+
.attributed(ids)
|
|
85
|
+
.ownedBy(DISPOSABLE_CHECK_ID)
|
|
86
|
+
.backend(DISPOSABLE_CHECK_ID, read.entity ?? "caller", "the disposable signup failed", `${register.operationId} returned ${signed.status}.`, [signed]);
|
|
87
|
+
input.grades.graded(ids, DISPOSABLE_CHECK_ID);
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
const disposable = { authorization: `Bearer ${token}` };
|
|
91
|
+
for (const op of ops) {
|
|
92
|
+
const blame = input.findings.attributed([op.operationId]).ownedBy(DISPOSABLE_CHECK_ID);
|
|
93
|
+
const sent = bodyFrom(requestSchemaOf(op, input.model), issued);
|
|
94
|
+
const called = await input.client.request(op.method, op.path, {
|
|
95
|
+
...(Object.keys(sent).length === 0 ? {} : { body: sent }),
|
|
96
|
+
headers: disposable,
|
|
97
|
+
operationId: op.operationId,
|
|
98
|
+
});
|
|
99
|
+
if (called.status < 200 || called.status >= 300) {
|
|
100
|
+
blame.backend(DISPOSABLE_CHECK_ID, op.entity ?? read.entity ?? "caller", "the disposable call failed", `${op.operationId} returned ${called.status} for the second user.`, [signed, called]);
|
|
101
|
+
input.grades.graded([op.operationId], DISPOSABLE_CHECK_ID);
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
const caller = await input.client.request(read.method, read.path, {
|
|
105
|
+
headers: input.auth,
|
|
106
|
+
operationId: read.operationId,
|
|
107
|
+
});
|
|
108
|
+
if (caller.status !== 200) {
|
|
109
|
+
blame.backend(DISPOSABLE_CHECK_ID, read.entity ?? "caller", "the suite caller was logged out", `${op.operationId} ran as the second user, and the suite ${read.operationId} then returned ${caller.status}.`, [called, caller]);
|
|
110
|
+
input.grades.graded([op.operationId], DISPOSABLE_CHECK_ID);
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
const endsSession = op.invalidates.some((route) => route.endsWith(read.path));
|
|
114
|
+
if (endsSession) {
|
|
115
|
+
const gone = await input.client.request(read.method, read.path, {
|
|
116
|
+
headers: disposable,
|
|
117
|
+
operationId: read.operationId,
|
|
118
|
+
});
|
|
119
|
+
if (gone.status !== 401) {
|
|
120
|
+
blame.backend(DISPOSABLE_CHECK_ID, op.entity ?? read.entity ?? "caller", "the disposable session was still accepted", `${op.operationId} invalidates ${read.operationId}, and that read then returned ${gone.status} with the second user's credential.`, [called, gone]);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
input.grades.graded([op.operationId], DISPOSABLE_CHECK_ID);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
function bodyFrom(schema, issued) {
|
|
127
|
+
if (schema === null)
|
|
128
|
+
return {};
|
|
129
|
+
const properties = schema.properties;
|
|
130
|
+
if (properties === null || typeof properties !== "object" || Array.isArray(properties))
|
|
131
|
+
return {};
|
|
132
|
+
const body = {};
|
|
133
|
+
for (const name of Object.keys(properties)) {
|
|
134
|
+
const value = issued[name];
|
|
135
|
+
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean")
|
|
136
|
+
body[name] = value;
|
|
137
|
+
}
|
|
138
|
+
return body;
|
|
139
|
+
}
|
|
140
|
+
export const disposableCheck = {
|
|
141
|
+
id: DISPOSABLE_CHECK_ID,
|
|
142
|
+
mutates: true,
|
|
143
|
+
needs: "x-destructive, a register operation, and a caller read",
|
|
144
|
+
plan: () => ({ ok: false }),
|
|
145
|
+
run: () => Promise.resolve(ASSERTED),
|
|
146
|
+
/* Destructive routes have no entity. `runDisposableOps` grades them after the entity loop. */
|
|
147
|
+
subjects: () => [],
|
|
148
|
+
};
|
|
149
|
+
//# sourceMappingURL=disposable.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"disposable.js","sourceRoot":"","sources":["../../src/runtime/disposable.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `x-entity.identity: root` — the path id is the signed-up caller's root.
|
|
3
|
+
* No list and no create. No token is a 401. The authed read uses that root.
|
|
4
|
+
*/
|
|
5
|
+
import type { Check } from "./checks.ts";
|
|
6
|
+
import type { Client } from "./client.ts";
|
|
7
|
+
import { type FindingCollector, type Outcome } from "./finding.ts";
|
|
8
|
+
import type { EntityModel, SpecModel } from "../spec/graph.ts";
|
|
9
|
+
export declare const ROOT_READ_CHECK_ID: "read.matches-caller-root";
|
|
10
|
+
export declare function rootReadIds(entity: EntityModel): string[];
|
|
11
|
+
export declare function runRootRead(input: {
|
|
12
|
+
auth: () => Record<string, string>;
|
|
13
|
+
client: Client;
|
|
14
|
+
entity: EntityModel;
|
|
15
|
+
findings: FindingCollector;
|
|
16
|
+
model: SpecModel;
|
|
17
|
+
roots: Record<string, string>;
|
|
18
|
+
}): Promise<Outcome>;
|
|
19
|
+
export declare const rootReadCheck: Check;
|
|
20
|
+
//# sourceMappingURL=root-read.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"root-read.d.ts","sourceRoot":"","sources":["../../src/runtime/root-read.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AACxC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACzC,OAAO,EAAY,KAAK,gBAAgB,EAAE,KAAK,OAAO,EAAE,MAAM,cAAc,CAAA;AAE5E,OAAO,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AAG9D,eAAO,MAAM,kBAAkB,4BAAwD,CAAA;AAEvF,wBAAgB,WAAW,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,EAAE,CAIzD;AAED,wBAAsB,WAAW,CAAC,KAAK,EAAE;IACxC,IAAI,EAAE,MAAM,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAClC,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,WAAW,CAAA;IACnB,QAAQ,EAAE,gBAAgB,CAAA;IAC1B,KAAK,EAAE,SAAS,CAAA;IAChB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAC7B,GAAG,OAAO,CAAC,OAAO,CAAC,CA4CnB;AAUD,eAAO,MAAM,aAAa,EAAE,KAO3B,CAAA"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `x-entity.identity: root` — the path id is the signed-up caller's root.
|
|
3
|
+
* No list and no create. No token is a 401. The authed read uses that root.
|
|
4
|
+
*/
|
|
5
|
+
import { ASSERTED } from "./finding.js";
|
|
6
|
+
import { fillPath } from "./world.js";
|
|
7
|
+
export const ROOT_READ_CHECK_ID = "read.matches-caller-root";
|
|
8
|
+
export function rootReadIds(entity) {
|
|
9
|
+
if (entity.identity !== "root")
|
|
10
|
+
return [];
|
|
11
|
+
if (entity.reads !== undefined && entity.reads.length > 0)
|
|
12
|
+
return entity.reads;
|
|
13
|
+
return entity.read === undefined ? [] : [entity.read];
|
|
14
|
+
}
|
|
15
|
+
export async function runRootRead(input) {
|
|
16
|
+
const ids = rootReadIds(input.entity);
|
|
17
|
+
const findings = input.findings.attributed(ids).ownedBy(ROOT_READ_CHECK_ID);
|
|
18
|
+
for (const id of ids) {
|
|
19
|
+
const op = input.model.byOperationId.get(id);
|
|
20
|
+
if (op === undefined)
|
|
21
|
+
continue;
|
|
22
|
+
let path;
|
|
23
|
+
try {
|
|
24
|
+
path = fillPath(op.path, input.roots);
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return findings.unresolved(ROOT_READ_CHECK_ID, input.entity.name, `${op.operationId} needs a path id`);
|
|
28
|
+
}
|
|
29
|
+
const query = dateWindow(op.queryParamNames);
|
|
30
|
+
const anonymous = await input.client.request(op.method, path, {
|
|
31
|
+
headers: {},
|
|
32
|
+
operationId: op.operationId,
|
|
33
|
+
...(query === undefined ? {} : { query }),
|
|
34
|
+
skipAuthRefresh: true,
|
|
35
|
+
});
|
|
36
|
+
if (anonymous.status !== 401) {
|
|
37
|
+
return findings.security(ROOT_READ_CHECK_ID, input.entity.name, "the parent read answered without a token", `${op.operationId} returned ${anonymous.status} with no credential.`, [anonymous]);
|
|
38
|
+
}
|
|
39
|
+
const read = await input.client.request(op.method, path, {
|
|
40
|
+
headers: input.auth,
|
|
41
|
+
operationId: op.operationId,
|
|
42
|
+
...(query === undefined ? {} : { query }),
|
|
43
|
+
});
|
|
44
|
+
if (read.status !== 200) {
|
|
45
|
+
return findings.backend(ROOT_READ_CHECK_ID, input.entity.name, "the parent read failed", `${op.operationId} returned ${read.status} for ${path}.`, [anonymous, read]);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return ASSERTED;
|
|
49
|
+
}
|
|
50
|
+
/** `from` is a week before `to` when a read asks for both. */
|
|
51
|
+
function dateWindow(names) {
|
|
52
|
+
if (!names.includes("from") || !names.includes("to"))
|
|
53
|
+
return undefined;
|
|
54
|
+
const to = new Date();
|
|
55
|
+
const from = new Date(to.getTime() - 7 * 24 * 60 * 60 * 1000);
|
|
56
|
+
return { from: from.toISOString(), to: to.toISOString() };
|
|
57
|
+
}
|
|
58
|
+
export const rootReadCheck = {
|
|
59
|
+
id: ROOT_READ_CHECK_ID,
|
|
60
|
+
needs: "x-entity.identity: root and a read",
|
|
61
|
+
plan: () => ({ ok: false }),
|
|
62
|
+
run: () => Promise.resolve(ASSERTED),
|
|
63
|
+
/* The entity loop calls `runRootRead` and returns. This entry keeps the id registered. */
|
|
64
|
+
subjects: () => [],
|
|
65
|
+
};
|
|
66
|
+
//# sourceMappingURL=root-read.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"root-read.js","sourceRoot":"","sources":["../../src/runtime/root-read.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,EAAE,QAAQ,EAAuC,MAAM,cAAc,CAAA;AAG5E,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAErC,MAAM,CAAC,MAAM,kBAAkB,GAAG,0BAAqD,CAAA;AAEvF,MAAM,UAAU,WAAW,CAAC,MAAmB;IAC9C,IAAI,MAAM,CAAC,QAAQ,KAAK,MAAM;QAAE,OAAO,EAAE,CAAA;IACzC,IAAI,MAAM,CAAC,KAAK,KAAK,SAAS,IAAI,MAAM,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,MAAM,CAAC,KAAK,CAAA;IAC9E,OAAO,MAAM,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;AACtD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,KAOjC;IACA,MAAM,GAAG,GAAG,WAAW,CAAC,KAAK,CAAC,MAAM,CAAC,CAAA;IACrC,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,kBAAkB,CAAC,CAAA;IAC3E,KAAK,MAAM,EAAE,IAAI,GAAG,EAAE,CAAC;QACtB,MAAM,EAAE,GAAG,KAAK,CAAC,KAAK,CAAC,aAAa,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;QAC5C,IAAI,EAAE,KAAK,SAAS;YAAE,SAAQ;QAC9B,IAAI,IAAY,CAAA;QAChB,IAAI,CAAC;YACJ,IAAI,GAAG,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,KAAK,CAAC,KAAK,CAAC,CAAA;QACtC,CAAC;QAAC,MAAM,CAAC;YACR,OAAO,QAAQ,CAAC,UAAU,CAAC,kBAAkB,EAAE,KAAK,CAAC,MAAM,CAAC,IAAI,EAAE,GAAG,EAAE,CAAC,WAAW,kBAAkB,CAAC,CAAA;QACvG,CAAC;QACD,MAAM,KAAK,GAAG,UAAU,CAAC,EAAE,CAAC,eAAe,CAAC,CAAA;QAC5C,MAAM,SAAS,GAAG,MAAM,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,IAAI,EAAE;YAC7D,OAAO,EAAE,EAAE;YACX,WAAW,EAAE,EAAE,CAAC,WAAW;YAC3B,GAAG,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC;YACzC,eAAe,EAAE,IAAI;SACrB,CAAC,CAAA;QACF,IAAI,SAAS,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;YAC9B,OAAO,QAAQ,CAAC,QAAQ,CACvB,kBAAkB,EAClB,KAAK,CAAC,MAAM,CAAC,IAAI,EACjB,0CAA0C,EAC1C,GAAG,EAAE,CAAC,WAAW,aAAa,SAAS,CAAC,MAAM,sBAAsB,EACpE,CAAC,SAAS,CAAC,CACX,CAAA;QACF,CAAC;QACD,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,IAAI,EAAE;YACxD,OAAO,EAAE,KAAK,CAAC,IAAI;YACnB,WAAW,EAAE,EAAE,CAAC,WAAW;YAC3B,GAAG,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC;SACzC,CAAC,CAAA;QACF,IAAI,IAAI,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;YACzB,OAAO,QAAQ,CAAC,OAAO,CACtB,kBAAkB,EAClB,KAAK,CAAC,MAAM,CAAC,IAAI,EACjB,wBAAwB,EACxB,GAAG,EAAE,CAAC,WAAW,aAAa,IAAI,CAAC,MAAM,QAAQ,IAAI,GAAG,EACxD,CAAC,SAAS,EAAE,IAAI,CAAC,CACjB,CAAA;QACF,CAAC;IACF,CAAC;IACD,OAAO,QAAQ,CAAA;AAChB,CAAC;AAED,8DAA8D;AAC9D,SAAS,UAAU,CAAC,KAAwB;IAC3C,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC;QAAE,OAAO,SAAS,CAAA;IACtE,MAAM,EAAE,GAAG,IAAI,IAAI,EAAE,CAAA;IACrB,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAA;IAC7D,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,WAAW,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,WAAW,EAAE,EAAE,CAAA;AAC1D,CAAC;AAED,MAAM,CAAC,MAAM,aAAa,GAAU;IACnC,EAAE,EAAE,kBAAkB;IACtB,KAAK,EAAE,oCAAoC;IAC3C,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC;IAC3B,GAAG,EAAE,GAAqB,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC;IACtD,0FAA0F;IAC1F,QAAQ,EAAE,GAAG,EAAE,CAAC,EAAE;CAClB,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../src/runtime/run.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,EAAc,KAAK,WAAW,EAAuB,KAAK,SAAS,EAAqB,MAAM,kBAAkB,CAAA;AAEvH,OAAO,KAAK,EACX,YAAY,EACZ,KAAK,EACL,UAAU,EACV,eAAe,EACf,aAAa,EACb,SAAS,EACT,WAAW,EACX,iBAAiB,EACjB,aAAa,EACb,OAAO,EACP,MAAM,4BAA4B,CAAA;AAGnC,OAAO,EAIN,KAAK,WAAW,EAMhB,MAAM,cAAc,CAAA;AAErB,OAAO,EAAE,KAAK,kBAAkB,EAA2C,MAAM,iBAAiB,CAAA;
|
|
1
|
+
{"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../src/runtime/run.ts"],"names":[],"mappings":"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"}
|
package/dist/runtime/run.js
CHANGED
|
@@ -16,6 +16,8 @@ import { resolveBackoff } from "./poll.js";
|
|
|
16
16
|
import { persistedToPrincipal, snapshotPrincipal } from "./principals.js";
|
|
17
17
|
import { CHECKS } from "./checks.js";
|
|
18
18
|
import { runPublicGets } from "./one-shot.js";
|
|
19
|
+
import { runDisposableOps } from "./disposable.js";
|
|
20
|
+
import { rootReadIds, ROOT_READ_CHECK_ID, runRootRead } from "./root-read.js";
|
|
19
21
|
import { entityIsSelf, runSelfIdentity, SELF_CHECK_ID, selfOperationIds } from "./self.js";
|
|
20
22
|
import { AuthRefreshError, Client, } from "./client.js";
|
|
21
23
|
import { FindingCollector, isRootCause } from "./finding.js";
|
|
@@ -501,6 +503,29 @@ export async function run(options) {
|
|
|
501
503
|
const testEntity = async (entity) => {
|
|
502
504
|
const listOp = model.byOperationId.get(entity.list ?? "");
|
|
503
505
|
const createOp = model.byOperationId.get(entity.create ?? "");
|
|
506
|
+
/* The path id is the caller's root. Nothing to create. */
|
|
507
|
+
if (listOp === undefined && rootReadIds(entity).length > 0) {
|
|
508
|
+
entitiesTested.push(entity.name);
|
|
509
|
+
currentEntity = entity.name;
|
|
510
|
+
currentCheck = ROOT_READ_CHECK_ID;
|
|
511
|
+
currentPhase = "test";
|
|
512
|
+
const ops = rootReadIds(entity);
|
|
513
|
+
checksRun.add(ROOT_READ_CHECK_ID);
|
|
514
|
+
const outcome = await runRootRead({
|
|
515
|
+
auth: alpha.headers,
|
|
516
|
+
client,
|
|
517
|
+
entity,
|
|
518
|
+
findings,
|
|
519
|
+
model,
|
|
520
|
+
roots: { ...options.roots, ...alpha.roots },
|
|
521
|
+
});
|
|
522
|
+
if (outcome.kind === "stood-down" || outcome.kind === "unresolved") {
|
|
523
|
+
grades.skipped(ops, outcome.reason);
|
|
524
|
+
return;
|
|
525
|
+
}
|
|
526
|
+
grades.graded(ops, ROOT_READ_CHECK_ID);
|
|
527
|
+
return;
|
|
528
|
+
}
|
|
504
529
|
/* The caller is the record. Nothing to list, and register is not a create. */
|
|
505
530
|
if (listOp === undefined) {
|
|
506
531
|
if (!entityIsSelf(entity))
|
|
@@ -509,7 +534,7 @@ export async function run(options) {
|
|
|
509
534
|
currentEntity = entity.name;
|
|
510
535
|
currentCheck = SELF_CHECK_ID;
|
|
511
536
|
currentPhase = "test";
|
|
512
|
-
const ops = selfOperationIds(entity);
|
|
537
|
+
const ops = selfOperationIds(entity, model);
|
|
513
538
|
checksRun.add(SELF_CHECK_ID);
|
|
514
539
|
const outcome = await runSelfIdentity({
|
|
515
540
|
auth: alpha.headers,
|
|
@@ -1205,6 +1230,19 @@ export async function run(options) {
|
|
|
1205
1230
|
* was created is still removed below, and the report still written. */
|
|
1206
1231
|
findings.blocked("run.error", "run", "testing stopped on an unexpected error", errorText(error));
|
|
1207
1232
|
}
|
|
1233
|
+
await runDisposableOps({
|
|
1234
|
+
auth: alpha.headers,
|
|
1235
|
+
checksRun,
|
|
1236
|
+
client,
|
|
1237
|
+
findings,
|
|
1238
|
+
grades,
|
|
1239
|
+
inScope: (operationId) => runScope.inScope(operationId),
|
|
1240
|
+
mode: runScope.mode,
|
|
1241
|
+
model,
|
|
1242
|
+
nonce,
|
|
1243
|
+
...(hooks.resolveInput === undefined ? {} : { resolveInput: hooks.resolveInput }),
|
|
1244
|
+
seed,
|
|
1245
|
+
});
|
|
1208
1246
|
/* Unwind after every check has run, never per case: a check may legitimately depend on records
|
|
1209
1247
|
* another one created, and tearing down early turns that into a phantom defect. */
|
|
1210
1248
|
currentPhase = "teardown";
|