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.
- package/LICENSE +18 -0
- package/README.md +732 -0
- package/action.yml +279 -0
- package/comber.config.example.json +64 -0
- package/dist/api/classify.js +99 -0
- package/dist/api/classify.js.map +1 -0
- package/dist/api/driver.js +235 -0
- package/dist/api/driver.js.map +1 -0
- package/dist/api/graphql-driver.js +150 -0
- package/dist/api/graphql-driver.js.map +1 -0
- package/dist/api/graphql.js +115 -0
- package/dist/api/graphql.js.map +1 -0
- package/dist/api/har-driver.js +167 -0
- package/dist/api/har-driver.js.map +1 -0
- package/dist/api/har.js +100 -0
- package/dist/api/har.js.map +1 -0
- package/dist/api/load.js +83 -0
- package/dist/api/load.js.map +1 -0
- package/dist/api/openapi.js +159 -0
- package/dist/api/openapi.js.map +1 -0
- package/dist/api/request.js +141 -0
- package/dist/api/request.js.map +1 -0
- package/dist/api/schema.js +126 -0
- package/dist/api/schema.js.map +1 -0
- package/dist/cli.js +352 -0
- package/dist/cli.js.map +1 -0
- package/dist/comment.js +164 -0
- package/dist/comment.js.map +1 -0
- package/dist/config.js +664 -0
- package/dist/config.js.map +1 -0
- package/dist/connectors/builtin/discord.js +55 -0
- package/dist/connectors/builtin/github-checks.js +53 -0
- package/dist/connectors/builtin/junit.js +70 -0
- package/dist/connectors/builtin/linear.js +58 -0
- package/dist/connectors/builtin/slack.js +60 -0
- package/dist/connectors/builtin/webhook.js +39 -0
- package/dist/connectors/index.js +3 -0
- package/dist/connectors/loader.js +72 -0
- package/dist/connectors/registry.js +28 -0
- package/dist/connectors/types.js +23 -0
- package/dist/core/action-gate.js +48 -0
- package/dist/core/baseline.js +85 -0
- package/dist/core/baseline.js.map +1 -0
- package/dist/core/blind-spots.js +22 -0
- package/dist/core/blind-spots.js.map +1 -0
- package/dist/core/contract-extras.js +1 -0
- package/dist/core/contract-extras.js.map +1 -0
- package/dist/core/contract.js +1 -0
- package/dist/core/contract.js.map +1 -0
- package/dist/core/coverage.js +70 -0
- package/dist/core/coverage.js.map +1 -0
- package/dist/core/crawl.js +229 -0
- package/dist/core/crawl.js.map +1 -0
- package/dist/core/egress.js +128 -0
- package/dist/core/egress.js.map +1 -0
- package/dist/core/ledger.js +41 -0
- package/dist/core/ledger.js.map +1 -0
- package/dist/core/net.js +39 -0
- package/dist/core/pipeline.js +191 -0
- package/dist/core/pipeline.js.map +1 -0
- package/dist/core/plan.js +121 -0
- package/dist/core/plan.js.map +1 -0
- package/dist/core/redact.js +240 -0
- package/dist/core/redact.js.map +1 -0
- package/dist/core/replay.js +263 -0
- package/dist/core/replay.js.map +1 -0
- package/dist/core/report.js +253 -0
- package/dist/core/report.js.map +1 -0
- package/dist/core/safety-judge.js +116 -0
- package/dist/core/safety.js +92 -0
- package/dist/core/safety.js.map +1 -0
- package/dist/core/signal-normalize.js +190 -0
- package/dist/core/signal-normalize.js.map +1 -0
- package/dist/core/signals.js +5 -0
- package/dist/core/signals.js.map +1 -0
- package/dist/core/triage.js +104 -0
- package/dist/core/triage.js.map +1 -0
- package/dist/core/types.js +1 -0
- package/dist/core/types.js.map +1 -0
- package/dist/desktop/desktop-profile.js +6 -0
- package/dist/desktop/desktop-profile.js.map +1 -0
- package/dist/desktop/driver.js +433 -0
- package/dist/desktop/driver.js.map +1 -0
- package/dist/desktop/evidence.js +206 -0
- package/dist/desktop/evidence.js.map +1 -0
- package/dist/desktop/macos/accessibility-runner.js +118 -0
- package/dist/desktop/macos/accessibility-runner.js.map +1 -0
- package/dist/desktop/macos/types.js +1 -0
- package/dist/desktop/macos/types.js.map +1 -0
- package/dist/desktop/preflight.js +107 -0
- package/dist/desktop/preflight.js.map +1 -0
- package/dist/desktop/proxy-proof.js +185 -0
- package/dist/desktop/proxy-proof.js.map +1 -0
- package/dist/desktop/view.js +348 -0
- package/dist/desktop/view.js.map +1 -0
- package/dist/desktop/windows/types.js +1 -0
- package/dist/desktop/windows/types.js.map +1 -0
- package/dist/desktop/windows/uia-runner.js +127 -0
- package/dist/desktop/windows/uia-runner.js.map +1 -0
- package/dist/native/apple.js +14 -0
- package/dist/native/apple.js.map +1 -0
- package/dist/native/device-profile.js +6 -0
- package/dist/native/device-profile.js.map +1 -0
- package/dist/native/driver.js +416 -0
- package/dist/native/driver.js.map +1 -0
- package/dist/native/evidence.js +164 -0
- package/dist/native/evidence.js.map +1 -0
- package/dist/native/load.js +9 -0
- package/dist/native/load.js.map +1 -0
- package/dist/native/maestro.js +153 -0
- package/dist/native/maestro.js.map +1 -0
- package/dist/native/preflight.js +72 -0
- package/dist/native/preflight.js.map +1 -0
- package/dist/native/proxy-proof.js +182 -0
- package/dist/native/proxy-proof.js.map +1 -0
- package/dist/native/samsung.js +34 -0
- package/dist/native/samsung.js.map +1 -0
- package/dist/native/signals.js +50 -0
- package/dist/native/signals.js.map +1 -0
- package/dist/native/view.js +139 -0
- package/dist/native/view.js.map +1 -0
- package/dist/profile.js +237 -0
- package/dist/profile.js.map +1 -0
- package/dist/sink.js +134 -0
- package/dist/sink.js.map +1 -0
- package/dist/sinks/file.js +49 -0
- package/dist/sinks/file.js.map +1 -0
- package/dist/sinks/http.js +38 -0
- package/dist/sinks/http.js.map +1 -0
- package/dist/sinks/index.js +5 -0
- package/dist/sinks/index.js.map +1 -0
- package/dist/sinks/multi.js +40 -0
- package/dist/sinks/multi.js.map +1 -0
- package/dist/sinks/paths.js +32 -0
- package/dist/sinks/paths.js.map +1 -0
- package/dist/sinks/record.js +58 -0
- package/dist/sinks/record.js.map +1 -0
- package/dist/sinks/types.js +1 -0
- package/dist/sinks/types.js.map +1 -0
- package/dist/web/breakage.js +210 -0
- package/dist/web/breakage.js.map +1 -0
- package/dist/web/controls.js +102 -0
- package/dist/web/controls.js.map +1 -0
- package/dist/web/driver.js +803 -0
- package/dist/web/driver.js.map +1 -0
- package/dist/web/elements.js +151 -0
- package/dist/web/elements.js.map +1 -0
- package/dist/web/fingerprint.js +13 -0
- package/dist/web/fingerprint.js.map +1 -0
- package/dist/web/forms.js +74 -0
- package/dist/web/forms.js.map +1 -0
- package/dist/web/links.js +194 -0
- package/dist/web/links.js.map +1 -0
- package/dist/web/types.js +1 -0
- package/dist/web/types.js.map +1 -0
- package/package.json +80 -0
- package/runtime/macos-accessibility-runner.js +275 -0
- 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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"}
|