comber 0.1.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.
Files changed (158) hide show
  1. package/LICENSE +18 -0
  2. package/README.md +732 -0
  3. package/action.yml +279 -0
  4. package/comber.config.example.json +64 -0
  5. package/dist/api/classify.js +99 -0
  6. package/dist/api/classify.js.map +1 -0
  7. package/dist/api/driver.js +235 -0
  8. package/dist/api/driver.js.map +1 -0
  9. package/dist/api/graphql-driver.js +150 -0
  10. package/dist/api/graphql-driver.js.map +1 -0
  11. package/dist/api/graphql.js +115 -0
  12. package/dist/api/graphql.js.map +1 -0
  13. package/dist/api/har-driver.js +167 -0
  14. package/dist/api/har-driver.js.map +1 -0
  15. package/dist/api/har.js +100 -0
  16. package/dist/api/har.js.map +1 -0
  17. package/dist/api/load.js +83 -0
  18. package/dist/api/load.js.map +1 -0
  19. package/dist/api/openapi.js +159 -0
  20. package/dist/api/openapi.js.map +1 -0
  21. package/dist/api/request.js +141 -0
  22. package/dist/api/request.js.map +1 -0
  23. package/dist/api/schema.js +126 -0
  24. package/dist/api/schema.js.map +1 -0
  25. package/dist/cli.js +352 -0
  26. package/dist/cli.js.map +1 -0
  27. package/dist/comment.js +164 -0
  28. package/dist/comment.js.map +1 -0
  29. package/dist/config.js +664 -0
  30. package/dist/config.js.map +1 -0
  31. package/dist/connectors/builtin/discord.js +55 -0
  32. package/dist/connectors/builtin/github-checks.js +53 -0
  33. package/dist/connectors/builtin/junit.js +70 -0
  34. package/dist/connectors/builtin/linear.js +58 -0
  35. package/dist/connectors/builtin/slack.js +60 -0
  36. package/dist/connectors/builtin/webhook.js +39 -0
  37. package/dist/connectors/index.js +3 -0
  38. package/dist/connectors/loader.js +72 -0
  39. package/dist/connectors/registry.js +28 -0
  40. package/dist/connectors/types.js +23 -0
  41. package/dist/core/action-gate.js +48 -0
  42. package/dist/core/baseline.js +85 -0
  43. package/dist/core/baseline.js.map +1 -0
  44. package/dist/core/blind-spots.js +22 -0
  45. package/dist/core/blind-spots.js.map +1 -0
  46. package/dist/core/contract-extras.js +1 -0
  47. package/dist/core/contract-extras.js.map +1 -0
  48. package/dist/core/contract.js +1 -0
  49. package/dist/core/contract.js.map +1 -0
  50. package/dist/core/coverage.js +70 -0
  51. package/dist/core/coverage.js.map +1 -0
  52. package/dist/core/crawl.js +229 -0
  53. package/dist/core/crawl.js.map +1 -0
  54. package/dist/core/egress.js +128 -0
  55. package/dist/core/egress.js.map +1 -0
  56. package/dist/core/ledger.js +41 -0
  57. package/dist/core/ledger.js.map +1 -0
  58. package/dist/core/net.js +39 -0
  59. package/dist/core/pipeline.js +191 -0
  60. package/dist/core/pipeline.js.map +1 -0
  61. package/dist/core/plan.js +121 -0
  62. package/dist/core/plan.js.map +1 -0
  63. package/dist/core/redact.js +240 -0
  64. package/dist/core/redact.js.map +1 -0
  65. package/dist/core/replay.js +263 -0
  66. package/dist/core/replay.js.map +1 -0
  67. package/dist/core/report.js +253 -0
  68. package/dist/core/report.js.map +1 -0
  69. package/dist/core/safety-judge.js +116 -0
  70. package/dist/core/safety.js +92 -0
  71. package/dist/core/safety.js.map +1 -0
  72. package/dist/core/signal-normalize.js +190 -0
  73. package/dist/core/signal-normalize.js.map +1 -0
  74. package/dist/core/signals.js +5 -0
  75. package/dist/core/signals.js.map +1 -0
  76. package/dist/core/triage.js +104 -0
  77. package/dist/core/triage.js.map +1 -0
  78. package/dist/core/types.js +1 -0
  79. package/dist/core/types.js.map +1 -0
  80. package/dist/desktop/desktop-profile.js +6 -0
  81. package/dist/desktop/desktop-profile.js.map +1 -0
  82. package/dist/desktop/driver.js +433 -0
  83. package/dist/desktop/driver.js.map +1 -0
  84. package/dist/desktop/evidence.js +206 -0
  85. package/dist/desktop/evidence.js.map +1 -0
  86. package/dist/desktop/macos/accessibility-runner.js +118 -0
  87. package/dist/desktop/macos/accessibility-runner.js.map +1 -0
  88. package/dist/desktop/macos/types.js +1 -0
  89. package/dist/desktop/macos/types.js.map +1 -0
  90. package/dist/desktop/preflight.js +107 -0
  91. package/dist/desktop/preflight.js.map +1 -0
  92. package/dist/desktop/proxy-proof.js +185 -0
  93. package/dist/desktop/proxy-proof.js.map +1 -0
  94. package/dist/desktop/view.js +348 -0
  95. package/dist/desktop/view.js.map +1 -0
  96. package/dist/desktop/windows/types.js +1 -0
  97. package/dist/desktop/windows/types.js.map +1 -0
  98. package/dist/desktop/windows/uia-runner.js +127 -0
  99. package/dist/desktop/windows/uia-runner.js.map +1 -0
  100. package/dist/native/apple.js +14 -0
  101. package/dist/native/apple.js.map +1 -0
  102. package/dist/native/device-profile.js +6 -0
  103. package/dist/native/device-profile.js.map +1 -0
  104. package/dist/native/driver.js +416 -0
  105. package/dist/native/driver.js.map +1 -0
  106. package/dist/native/evidence.js +164 -0
  107. package/dist/native/evidence.js.map +1 -0
  108. package/dist/native/load.js +9 -0
  109. package/dist/native/load.js.map +1 -0
  110. package/dist/native/maestro.js +153 -0
  111. package/dist/native/maestro.js.map +1 -0
  112. package/dist/native/preflight.js +72 -0
  113. package/dist/native/preflight.js.map +1 -0
  114. package/dist/native/proxy-proof.js +182 -0
  115. package/dist/native/proxy-proof.js.map +1 -0
  116. package/dist/native/samsung.js +34 -0
  117. package/dist/native/samsung.js.map +1 -0
  118. package/dist/native/signals.js +50 -0
  119. package/dist/native/signals.js.map +1 -0
  120. package/dist/native/view.js +139 -0
  121. package/dist/native/view.js.map +1 -0
  122. package/dist/profile.js +237 -0
  123. package/dist/profile.js.map +1 -0
  124. package/dist/sink.js +134 -0
  125. package/dist/sink.js.map +1 -0
  126. package/dist/sinks/file.js +49 -0
  127. package/dist/sinks/file.js.map +1 -0
  128. package/dist/sinks/http.js +38 -0
  129. package/dist/sinks/http.js.map +1 -0
  130. package/dist/sinks/index.js +5 -0
  131. package/dist/sinks/index.js.map +1 -0
  132. package/dist/sinks/multi.js +40 -0
  133. package/dist/sinks/multi.js.map +1 -0
  134. package/dist/sinks/paths.js +32 -0
  135. package/dist/sinks/paths.js.map +1 -0
  136. package/dist/sinks/record.js +58 -0
  137. package/dist/sinks/record.js.map +1 -0
  138. package/dist/sinks/types.js +1 -0
  139. package/dist/sinks/types.js.map +1 -0
  140. package/dist/web/breakage.js +210 -0
  141. package/dist/web/breakage.js.map +1 -0
  142. package/dist/web/controls.js +102 -0
  143. package/dist/web/controls.js.map +1 -0
  144. package/dist/web/driver.js +803 -0
  145. package/dist/web/driver.js.map +1 -0
  146. package/dist/web/elements.js +151 -0
  147. package/dist/web/elements.js.map +1 -0
  148. package/dist/web/fingerprint.js +13 -0
  149. package/dist/web/fingerprint.js.map +1 -0
  150. package/dist/web/forms.js +74 -0
  151. package/dist/web/forms.js.map +1 -0
  152. package/dist/web/links.js +194 -0
  153. package/dist/web/links.js.map +1 -0
  154. package/dist/web/types.js +1 -0
  155. package/dist/web/types.js.map +1 -0
  156. package/package.json +80 -0
  157. package/runtime/macos-accessibility-runner.js +275 -0
  158. package/runtime/windows-uia-runner.ps1 +539 -0
@@ -0,0 +1,235 @@
1
+ import { operationLabel, parseOpenApi } from "./openapi.js";
2
+ import { buildRequest } from "./request.js";
3
+ import { apiSignal, classifyResponse } from "./classify.js";
4
+ import { ResponseValidator } from "./schema.js";
5
+ import { guardedFetch } from "../core/net.js";
6
+ /** HTTP methods Comber considers safe to fire by default (read-only). */
7
+ const SAFE_METHODS = new Set(["GET", "HEAD", "OPTIONS"]);
8
+ /** Default transport: the guarded fetch. Egress is fail-closed (private/metadata blocked
9
+ * unless `allowPrivateTarget`) and every request has a deadline. Reads the body once. */
10
+ function makeFetchTransport(guard) {
11
+ return async (req) => {
12
+ const res = await guardedFetch(req.url, { method: req.method, headers: req.headers, body: req.body }, guard);
13
+ const headers = {};
14
+ res.headers.forEach((v, k) => {
15
+ headers[k.toLowerCase()] = v;
16
+ });
17
+ return { status: res.status, headers, bodyText: await res.text() };
18
+ };
19
+ }
20
+ function headerOf(headers, name) {
21
+ const lower = name.toLowerCase();
22
+ for (const [k, v] of Object.entries(headers))
23
+ if (k.toLowerCase() === lower)
24
+ return v;
25
+ return undefined;
26
+ }
27
+ const NOOP_COVERAGE = { skip() { }, bumpExtra() { } };
28
+ export class ApiDriver {
29
+ opts;
30
+ name = "api";
31
+ capabilities = { screenshot: false, probes: [] };
32
+ base;
33
+ operations;
34
+ resolver;
35
+ validator;
36
+ title;
37
+ transport;
38
+ cov = NOOP_COVERAGE;
39
+ exercised = 0;
40
+ idorChecks = 0;
41
+ idorHits = 0;
42
+ constructor(opts) {
43
+ this.opts = opts;
44
+ const parsed = parseOpenApi(opts.doc);
45
+ this.operations = parsed.operations;
46
+ this.resolver = parsed.resolver;
47
+ this.validator = new ResponseValidator(parsed.resolver);
48
+ this.title = parsed.title;
49
+ this.base = opts.base.replace(/\/+$/, "");
50
+ this.transport =
51
+ opts.transport ?? makeFetchTransport({ allowPrivate: opts.allowPrivateTarget, timeoutMs: opts.timeoutMs });
52
+ }
53
+ // -- DriverExtras: the core injects its coverage sink so driver sub-metrics + skips land
54
+ // in the one honest ledger the orchestrator owns. --
55
+ attachCoverage(sink) {
56
+ this.cov = sink;
57
+ }
58
+ async start() {
59
+ /* stateless — nothing to launch (no browser/session). */
60
+ }
61
+ async close() {
62
+ /* nothing to tear down. */
63
+ }
64
+ // -- addressing: the API is ONE catalog state; the base IS the only address. --
65
+ get entry() {
66
+ return { id: this.base, scope: this.base, label: this.base };
67
+ }
68
+ entryAddress() {
69
+ return this.entry;
70
+ }
71
+ currentAddress() {
72
+ return this.entry;
73
+ }
74
+ inScope(addr) {
75
+ return addr.id === this.base;
76
+ }
77
+ async goto(_addr) {
78
+ /* single state — navigation is a no-op. */
79
+ }
80
+ /** DISCOVER — one Surface, one node per operation. Write methods are surfaced as
81
+ * `disabled` in read-only mode so the core counts them as a coverage skip (not a gap). */
82
+ async snapshot() {
83
+ const nodes = this.operations.map((op) => ({
84
+ id: operationLabel(op),
85
+ kind: "operation",
86
+ label: operationLabel(op),
87
+ handle: op,
88
+ disabled: !SAFE_METHODS.has(op.method) && !this.opts.allowWrites,
89
+ }));
90
+ return { address: this.entry, title: this.title, nodes, blindSpots: 0 };
91
+ }
92
+ /** EXERCISE — build a concrete request, send it, classify breakage; for auth-required
93
+ * operations also fire a no-credential probe to catch an auth bypass. */
94
+ async exercise(node) {
95
+ const op = node.handle;
96
+ // Defense in depth: the core already skips disabled write nodes, but never fire a write
97
+ // here unless writes are explicitly allowed.
98
+ if (!SAFE_METHODS.has(op.method) && !this.opts.allowWrites)
99
+ return {};
100
+ const req = buildRequest(this.base, op, this.resolver, this.opts.seed);
101
+ const signals = [];
102
+ let main;
103
+ try {
104
+ main = await this.send(req, true);
105
+ }
106
+ catch (e) {
107
+ this.exercised++;
108
+ signals.push(apiSignal(op, "request-failed", "warn", `${operationLabel(op)} → transport error: ${errMessage(e)}`, req.url));
109
+ return { signals };
110
+ }
111
+ signals.push(...classifyResponse(op, this.toView(main), req.url, this.resolver, this.validator));
112
+ // broken-auth: an operation that REQUIRES auth that returns 2xx WITHOUT a credential is
113
+ // an auth bypass. If a token is configured, fire an explicit no-credential probe; if no
114
+ // token is configured the main request already carried none, so reuse it.
115
+ if (op.requiresAuth) {
116
+ let probe = main;
117
+ if (this.opts.token) {
118
+ try {
119
+ probe = await this.send(req, false);
120
+ }
121
+ catch {
122
+ probe = undefined;
123
+ }
124
+ }
125
+ if (probe && probe.status >= 200 && probe.status < 300) {
126
+ signals.push(apiSignal(op, "broken-auth", "fail", `${operationLabel(op)} requires auth but returned HTTP ${probe.status} WITHOUT credentials (auth bypass)`, req.url));
127
+ }
128
+ }
129
+ // Multi-role cross-tenant object access (IDOR). Opt-in + sandbox-only: only fires for an
130
+ // operation the operator marked owner-scoped, when >= 2 named auth profiles are configured.
131
+ signals.push(...(await this.idorProbe(op, req.url)));
132
+ this.exercised++;
133
+ return { signals };
134
+ }
135
+ /**
136
+ * IDOR / broken-object-authorization probe. For an owner-scoped operation we take a resource
137
+ * OWNED by role B (B's role seed), confirm B can actually read it (2xx — so we don't flag a
138
+ * 404), then re-issue the SAME request with role A's credential. A 2xx for A is a cross-role
139
+ * object-access break: A reached a resource it does not own. Read-only-by-default still holds
140
+ * (a write owner-scoped op only reaches here when `allowWrites`, gated at the top of exercise).
141
+ */
142
+ async idorProbe(op, where) {
143
+ const profiles = this.opts.authProfiles ?? [];
144
+ if (profiles.length < 2 || !this.opts.ownerScoped?.has(operationLabel(op)))
145
+ return [];
146
+ const roleSeed = this.opts.roleSeed ?? {};
147
+ const out = [];
148
+ for (const owner of profiles) {
149
+ const ownerReq = buildRequest(this.base, op, this.resolver, roleSeed[owner.role]);
150
+ let ownerRes;
151
+ try {
152
+ ownerRes = await this.sendWithToken(ownerReq, owner.token);
153
+ }
154
+ catch {
155
+ continue;
156
+ }
157
+ if (!(ownerRes.status >= 200 && ownerRes.status < 300))
158
+ continue; // resource absent/not owned
159
+ for (const accessor of profiles) {
160
+ if (accessor.role === owner.role)
161
+ continue;
162
+ this.idorChecks++;
163
+ let accessorRes;
164
+ try {
165
+ accessorRes = await this.sendWithToken(ownerReq, accessor.token);
166
+ }
167
+ catch {
168
+ continue;
169
+ }
170
+ if (accessorRes.status >= 200 && accessorRes.status < 300) {
171
+ this.idorHits++;
172
+ out.push(apiSignal(op, "broken-object-authorization", "fail", `${operationLabel(op)} → role "${accessor.role}" accessed a resource owned by role "${owner.role}" ` +
173
+ `(HTTP ${accessorRes.status}) — IDOR / broken object-level authorization`, where));
174
+ }
175
+ }
176
+ }
177
+ return out;
178
+ }
179
+ /** Send a request through the transport. The Bearer credential is attached only when
180
+ * `auth` and a token are present; it is never logged. */
181
+ async send(req, auth) {
182
+ const headers = { accept: "application/json", ...req.headers };
183
+ if (req.bodyContentType)
184
+ headers["content-type"] = req.bodyContentType;
185
+ if (auth && this.opts.token)
186
+ headers["authorization"] = `Bearer ${this.opts.token}`;
187
+ return this.transport({ method: req.method, url: req.url, headers, body: req.body });
188
+ }
189
+ /** Send a request carrying a SPECIFIC role's Bearer credential (the multi-role harness). The
190
+ * token is attached as a header only; like all credentials it is never logged. */
191
+ async sendWithToken(req, token) {
192
+ const headers = { accept: "application/json", ...req.headers };
193
+ if (req.bodyContentType)
194
+ headers["content-type"] = req.bodyContentType;
195
+ headers["authorization"] = `Bearer ${token}`;
196
+ return this.transport({ method: req.method, url: req.url, headers, body: req.body });
197
+ }
198
+ toView(res) {
199
+ return { status: res.status, contentType: headerOf(res.headers, "content-type"), bodyText: res.bodyText };
200
+ }
201
+ /** No goto-time signals to drain (signals ride the ExerciseResult). */
202
+ drainSignals() {
203
+ return [];
204
+ }
205
+ /** No visual evidence for an API target. */
206
+ async screenshot() {
207
+ return Buffer.alloc(0);
208
+ }
209
+ /** No frontier expansion — exhaustiveness is exercising every operation node. */
210
+ outboundAddresses() {
211
+ return [];
212
+ }
213
+ /** Per-driver structured audit data merged into CrawlResult.extra (counts only — no
214
+ * secrets; the base is already surfaced as the redacted startUrl elsewhere). */
215
+ auditExtra() {
216
+ const writeOps = this.operations.filter((op) => !SAFE_METHODS.has(op.method)).length;
217
+ return {
218
+ apiAudit: {
219
+ operations: this.operations.length,
220
+ safeOperations: this.operations.length - writeOps,
221
+ writeOperations: writeOps,
222
+ exercised: this.exercised,
223
+ writesSkipped: this.opts.allowWrites ? 0 : writeOps,
224
+ allowWrites: this.opts.allowWrites,
225
+ authProfiles: this.opts.authProfiles?.length ?? 0,
226
+ ownerScopedOps: this.opts.ownerScoped?.size ?? 0,
227
+ idorChecks: this.idorChecks,
228
+ idorHits: this.idorHits,
229
+ },
230
+ };
231
+ }
232
+ }
233
+ function errMessage(e) {
234
+ return (e instanceof Error ? e.message : String(e)).slice(0, 160);
235
+ }
@@ -0,0 +1 @@
1
+ {"version":3,"file":"driver.js","sourceRoot":"","sources":["../../src/api/driver.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,150 @@
1
+ import { guardedFetch } from "../core/net.js";
2
+ import { INTROSPECTION_QUERY, buildOperationDocument, graphqlOperationLabel, parseIntrospection, } from "./graphql.js";
3
+ function makeFetchTransport(guard) {
4
+ return async (req) => {
5
+ const res = await guardedFetch(req.url, { method: req.method, headers: req.headers, body: req.body }, guard);
6
+ const headers = {};
7
+ res.headers.forEach((v, k) => {
8
+ headers[k.toLowerCase()] = v;
9
+ });
10
+ return { status: res.status, headers, bodyText: await res.text() };
11
+ };
12
+ }
13
+ const NOOP_COVERAGE = { skip() { }, bumpExtra() { } };
14
+ function signatureOf(op, kind) {
15
+ return `${kind}|${graphqlOperationLabel(op)}`.slice(0, 80);
16
+ }
17
+ export class GraphqlDriver {
18
+ opts;
19
+ name = "graphql";
20
+ capabilities = { screenshot: false, probes: [] };
21
+ endpoint;
22
+ transport;
23
+ schema = { operations: [], typeMap: new Map() };
24
+ cov = NOOP_COVERAGE;
25
+ exercised = 0;
26
+ constructor(opts) {
27
+ this.opts = opts;
28
+ this.endpoint = opts.endpoint;
29
+ this.transport =
30
+ opts.transport ?? makeFetchTransport({ allowPrivate: opts.allowPrivateTarget, timeoutMs: opts.timeoutMs });
31
+ }
32
+ attachCoverage(sink) {
33
+ this.cov = sink;
34
+ }
35
+ /** DISCOVER the surface: introspect (over the transport) unless an introspection result was
36
+ * supplied, then parse `__schema` into the operation set. */
37
+ async start() {
38
+ let introspection = this.opts.introspection;
39
+ if (introspection === undefined) {
40
+ const res = await this.post(JSON.stringify({ query: INTROSPECTION_QUERY }));
41
+ introspection = safeJson(res.bodyText);
42
+ }
43
+ this.schema = parseIntrospection(introspection);
44
+ }
45
+ async close() {
46
+ /* stateless */
47
+ }
48
+ get entry() {
49
+ return { id: this.endpoint, scope: this.endpoint, label: this.endpoint };
50
+ }
51
+ entryAddress() {
52
+ return this.entry;
53
+ }
54
+ currentAddress() {
55
+ return this.entry;
56
+ }
57
+ inScope(addr) {
58
+ return addr.id === this.endpoint;
59
+ }
60
+ async goto(_addr) {
61
+ /* single state */
62
+ }
63
+ /** One Surface; one node per operation. Mutation (write) nodes are surfaced `disabled` in
64
+ * read-only mode so the core counts them as a coverage skip, not a gap. */
65
+ async snapshot() {
66
+ const nodes = this.schema.operations.map((op) => ({
67
+ id: graphqlOperationLabel(op),
68
+ kind: "graphql-operation",
69
+ label: graphqlOperationLabel(op),
70
+ handle: op,
71
+ disabled: op.kind === "mutation" && !this.opts.allowWrites,
72
+ }));
73
+ return { address: this.entry, title: "GraphQL", nodes, blindSpots: 0 };
74
+ }
75
+ /** EXERCISE — POST a minimal valid operation document and classify the result. */
76
+ async exercise(node) {
77
+ const op = node.handle;
78
+ // Defense in depth: never send a mutation unless writes are explicitly allowed.
79
+ if (op.kind === "mutation" && !this.opts.allowWrites)
80
+ return {};
81
+ const doc = buildOperationDocument(op, this.schema.typeMap);
82
+ const signals = [];
83
+ let res;
84
+ try {
85
+ res = await this.post(JSON.stringify({ query: doc }));
86
+ }
87
+ catch (e) {
88
+ this.exercised++;
89
+ signals.push(this.signal(op, "request-failed", "warn", `${graphqlOperationLabel(op)} → transport error: ${errMessage(e)}`));
90
+ return { signals };
91
+ }
92
+ this.exercised++;
93
+ if (res.status !== 200) {
94
+ signals.push(this.signal(op, "graphql-http-status", res.status >= 500 ? "fail" : "warn", `${graphqlOperationLabel(op)} → HTTP ${res.status} (GraphQL normally answers 200)`));
95
+ }
96
+ const body = safeJson(res.bodyText);
97
+ const errors = body && typeof body === "object" ? body.errors : undefined;
98
+ if (Array.isArray(errors) && errors.length > 0) {
99
+ const first = errors
100
+ .slice(0, 3)
101
+ .map((e) => (e && typeof e === "object" ? String(e.message ?? "error") : String(e)))
102
+ .join("; ")
103
+ .slice(0, 160);
104
+ signals.push(this.signal(op, "graphql-error", "fail", `${graphqlOperationLabel(op)} → GraphQL errors: ${first}`));
105
+ }
106
+ return { signals };
107
+ }
108
+ signal(op, kind, severity, message) {
109
+ return { kind, severity, message, url: this.endpoint, signature: signatureOf(op, kind), action: graphqlOperationLabel(op) };
110
+ }
111
+ async post(body) {
112
+ const headers = { accept: "application/json", "content-type": "application/json" };
113
+ if (this.opts.token)
114
+ headers["authorization"] = `Bearer ${this.opts.token}`;
115
+ return this.transport({ method: "POST", url: this.endpoint, headers, body });
116
+ }
117
+ drainSignals() {
118
+ return [];
119
+ }
120
+ async screenshot() {
121
+ return Buffer.alloc(0);
122
+ }
123
+ outboundAddresses() {
124
+ return [];
125
+ }
126
+ auditExtra() {
127
+ const mutations = this.schema.operations.filter((o) => o.kind === "mutation").length;
128
+ return {
129
+ graphqlAudit: {
130
+ operations: this.schema.operations.length,
131
+ queries: this.schema.operations.length - mutations,
132
+ mutations,
133
+ exercised: this.exercised,
134
+ mutationsSkipped: this.opts.allowWrites ? 0 : mutations,
135
+ allowWrites: this.opts.allowWrites,
136
+ },
137
+ };
138
+ }
139
+ }
140
+ function safeJson(text) {
141
+ try {
142
+ return JSON.parse(text);
143
+ }
144
+ catch {
145
+ return undefined;
146
+ }
147
+ }
148
+ function errMessage(e) {
149
+ return (e instanceof Error ? e.message : String(e)).slice(0, 160);
150
+ }
@@ -0,0 +1 @@
1
+ {"version":3,"file":"graphql-driver.js","sourceRoot":"","sources":["../../src/api/graphql-driver.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,115 @@
1
+ // GraphQL-introspection spec source — the API driver's THIRD discovery mode. Given a GraphQL
2
+ // endpoint we send the standard introspection query, parse `__schema`, and surface the Query
3
+ // and Mutation fields as exercisable operations. No `graphql` dependency: introspection is
4
+ // plain JSON over the wire and a minimal valid operation document is a small string we build
5
+ // from the introspected types. Leaf module — imports nothing from `core`/`web`.
6
+ /** The standard GraphQL introspection query (queries + mutations + the type/enum/input shapes
7
+ * we need to synthesize a minimal valid operation document). Trimmed to what the checker uses. */
8
+ export const INTROSPECTION_QUERY = `query IntrospectionQuery {
9
+ __schema {
10
+ queryType { name }
11
+ mutationType { name }
12
+ types {
13
+ kind
14
+ name
15
+ fields(includeDeprecated: true) { name args { name type { ...TypeRef } } type { ...TypeRef } }
16
+ inputFields { name type { ...TypeRef } }
17
+ enumValues(includeDeprecated: true) { name }
18
+ }
19
+ }
20
+ }
21
+ fragment TypeRef on __Type {
22
+ kind name
23
+ ofType { kind name ofType { kind name ofType { kind name ofType { kind name } } } }
24
+ }`;
25
+ function asObject(v) {
26
+ return v && typeof v === "object" && !Array.isArray(v) ? v : {};
27
+ }
28
+ /** Unwrap NON_NULL/LIST wrappers down to the underlying named type ref. */
29
+ function namedRef(t) {
30
+ let cur = t ?? {};
31
+ let guard = 0;
32
+ while (cur && cur.ofType && guard++ < 16)
33
+ cur = cur.ofType;
34
+ return cur;
35
+ }
36
+ const isRequired = (t) => t?.kind === "NON_NULL";
37
+ const stripNonNull = (t) => (t.kind === "NON_NULL" && t.ofType ? t.ofType : t);
38
+ /**
39
+ * Parse an introspection RESULT (`{ data: { __schema } }` or a bare `{ __schema }`) into the
40
+ * Query+Mutation operations plus a name→type map (for building minimal operation documents).
41
+ */
42
+ export function parseIntrospection(result) {
43
+ const root = asObject(result);
44
+ const data = asObject(root.data ?? root);
45
+ const schema = asObject(data.__schema);
46
+ const types = Array.isArray(schema.types) ? schema.types : [];
47
+ const typeMap = new Map();
48
+ for (const t of types)
49
+ if (typeof t?.name === "string")
50
+ typeMap.set(t.name, t);
51
+ const queryTypeName = asObject(schema.queryType).name;
52
+ const mutationTypeName = asObject(schema.mutationType).name;
53
+ const operations = [];
54
+ const collect = (typeName, kind) => {
55
+ if (typeof typeName !== "string")
56
+ return;
57
+ const t = typeMap.get(typeName);
58
+ for (const f of t?.fields ?? []) {
59
+ // Skip introspection meta-fields if a server lists them.
60
+ if (f.name.startsWith("__"))
61
+ continue;
62
+ operations.push({ kind, name: f.name, args: f.args ?? [], returnType: f.type });
63
+ }
64
+ };
65
+ collect(queryTypeName, "query");
66
+ collect(mutationTypeName, "mutation");
67
+ return { operations, typeMap };
68
+ }
69
+ /** A minimal literal satisfying an argument/input-field type (deterministic). */
70
+ function argLiteral(typeRef, typeMap, depth = 0) {
71
+ const outer = stripNonNull(typeRef);
72
+ if (outer.kind === "LIST")
73
+ return "[]"; // an empty list satisfies `[T]` and `[T]!`
74
+ const named = namedRef(typeRef);
75
+ const t = named.name ? typeMap.get(named.name) : undefined;
76
+ switch (named.name) {
77
+ case "Int":
78
+ case "Float":
79
+ return "1";
80
+ case "Boolean":
81
+ return "true";
82
+ case "ID":
83
+ case "String":
84
+ return JSON.stringify("sample");
85
+ }
86
+ if (t?.kind === "ENUM")
87
+ return t.enumValues?.[0]?.name ?? "null"; // enum literals are unquoted
88
+ if (t?.kind === "INPUT_OBJECT") {
89
+ if (depth >= 4)
90
+ return "{}";
91
+ const required = (t.inputFields ?? []).filter((f) => isRequired(f.type));
92
+ return `{${required.map((f) => `${f.name}: ${argLiteral(f.type, typeMap, depth + 1)}`).join(", ")}}`;
93
+ }
94
+ return JSON.stringify("sample"); // custom scalar — best effort
95
+ }
96
+ /**
97
+ * Build a minimal VALID operation document for `op`: include only the REQUIRED (NON_NULL) args
98
+ * with synthesized literals, and request `{ __typename }` when the return type is composite (an
99
+ * object/interface/union needs a sub-selection); scalars/enums take no selection set.
100
+ */
101
+ export function buildOperationDocument(op, typeMap) {
102
+ const required = op.args.filter((a) => isRequired(a.type));
103
+ const argStr = required.length
104
+ ? `(${required.map((a) => `${a.name}: ${argLiteral(a.type, typeMap)}`).join(", ")})`
105
+ : "";
106
+ const named = namedRef(op.returnType);
107
+ const rt = named.name ? typeMap.get(named.name) : undefined;
108
+ const composite = rt?.kind === "OBJECT" || rt?.kind === "INTERFACE" || rt?.kind === "UNION";
109
+ const selection = composite ? " { __typename }" : "";
110
+ return `${op.kind} { ${op.name}${argStr}${selection} }`;
111
+ }
112
+ /** "query me" / "mutation createUser" — stable operation identity (node id/label, dedupe ns). */
113
+ export function graphqlOperationLabel(op) {
114
+ return `${op.kind} ${op.name}`;
115
+ }
@@ -0,0 +1 @@
1
+ {"version":3,"file":"graphql.js","sourceRoot":"","sources":["../../src/api/graphql.ts"],"names":[],"mappings":"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"}