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,433 @@
|
|
|
1
|
+
import { writeFile } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { buildDesktopEvidence } from "./evidence.js";
|
|
4
|
+
import { evaluateDesktopReadiness } from "./preflight.js";
|
|
5
|
+
import { blindSpotDetailsOf, macosAccessibilityBlindSpotDetailsOf, macosAccessibilityScreenNameOf, macosAccessibilityScreenSignature, macosAccessibilityViewToSurfaceNodes, viewToSurfaceNodes, windowsScreenNameOf, windowsScreenSignature, } from "./view.js";
|
|
6
|
+
const LAUNCH_ID = "launch";
|
|
7
|
+
const UIA_PATH_PREFIX = "uiapath:";
|
|
8
|
+
const AX_PATH_PREFIX = "axpath:";
|
|
9
|
+
const BASE_SEP = "::";
|
|
10
|
+
const ACTION_SEP = ">>";
|
|
11
|
+
const PROBE_TEXT = "comber-probe";
|
|
12
|
+
const NOOP_COVERAGE = { skip() { }, bumpExtra() { } };
|
|
13
|
+
export class DesktopDriver {
|
|
14
|
+
name = "desktop";
|
|
15
|
+
capabilities = {
|
|
16
|
+
screenshot: false,
|
|
17
|
+
probes: [
|
|
18
|
+
{ name: "session", slot: "after-goto" },
|
|
19
|
+
{ name: "dom", slot: "after-snapshot" },
|
|
20
|
+
{ name: "crash", slot: "after-exercise" },
|
|
21
|
+
],
|
|
22
|
+
};
|
|
23
|
+
platform;
|
|
24
|
+
runner;
|
|
25
|
+
launchTarget;
|
|
26
|
+
desktopProfile;
|
|
27
|
+
cov = NOOP_COVERAGE;
|
|
28
|
+
signals = [];
|
|
29
|
+
anchor = { id: LAUNCH_ID, scope: LAUNCH_ID, label: "launch" };
|
|
30
|
+
baseAnchorId = LAUNCH_ID;
|
|
31
|
+
currentActions = [];
|
|
32
|
+
drifted = false;
|
|
33
|
+
justWentTo = false;
|
|
34
|
+
anchorFp = "";
|
|
35
|
+
currentScope = LAUNCH_ID;
|
|
36
|
+
lastTree;
|
|
37
|
+
controlsExercised = 0;
|
|
38
|
+
desktopEvidenceGapsReported = false;
|
|
39
|
+
observedAutomationTreeArtifacts = new Set();
|
|
40
|
+
runDir;
|
|
41
|
+
snapshotArtifactCount = 0;
|
|
42
|
+
constructor(opts) {
|
|
43
|
+
const windowsTargetFields = {};
|
|
44
|
+
if (opts.platform === "windows") {
|
|
45
|
+
if (opts.processId !== undefined)
|
|
46
|
+
windowsTargetFields.processId = opts.processId;
|
|
47
|
+
if (opts.windowTitle !== undefined)
|
|
48
|
+
windowsTargetFields.windowTitle = opts.windowTitle;
|
|
49
|
+
}
|
|
50
|
+
this.platform = opts.platform;
|
|
51
|
+
this.runner = opts.runner;
|
|
52
|
+
this.launchTarget = {
|
|
53
|
+
...(opts.appId !== undefined ? { appId: opts.appId } : {}),
|
|
54
|
+
...(opts.appPath !== undefined ? { appPath: opts.appPath } : {}),
|
|
55
|
+
...(opts.processName !== undefined ? { processName: opts.processName } : {}),
|
|
56
|
+
...windowsTargetFields,
|
|
57
|
+
};
|
|
58
|
+
this.desktopProfile =
|
|
59
|
+
opts.desktopProfile ?? {
|
|
60
|
+
platform: opts.platform,
|
|
61
|
+
...(opts.appId !== undefined ? { appId: opts.appId } : {}),
|
|
62
|
+
...(opts.appPath !== undefined ? { appPath: opts.appPath } : {}),
|
|
63
|
+
...(opts.processName !== undefined ? { processName: opts.processName } : {}),
|
|
64
|
+
...windowsTargetFields,
|
|
65
|
+
automationBackend: opts.platform === "windows" ? "uia" : "accessibility",
|
|
66
|
+
};
|
|
67
|
+
this.capabilities = {
|
|
68
|
+
...this.capabilities,
|
|
69
|
+
screenshot: typeof this.runner.screenshot === "function",
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
attachCoverage(sink) {
|
|
73
|
+
this.cov = sink;
|
|
74
|
+
}
|
|
75
|
+
attachRunDir(runDir) {
|
|
76
|
+
this.runDir = runDir;
|
|
77
|
+
}
|
|
78
|
+
async start() {
|
|
79
|
+
/* Session establishment is done by goto(), matching the native driver's relaunch model. */
|
|
80
|
+
}
|
|
81
|
+
async close() {
|
|
82
|
+
await this.runner.close().catch(() => { });
|
|
83
|
+
}
|
|
84
|
+
entryAddress() {
|
|
85
|
+
return { id: LAUNCH_ID, scope: LAUNCH_ID, label: "launch" };
|
|
86
|
+
}
|
|
87
|
+
currentAddress() {
|
|
88
|
+
if (!this.drifted)
|
|
89
|
+
return this.anchor;
|
|
90
|
+
return { id: `${this.anchor.id}#drifted`, scope: this.anchor.scope, label: "(navigated away)" };
|
|
91
|
+
}
|
|
92
|
+
inScope(addr) {
|
|
93
|
+
return addr.id === LAUNCH_ID || addr.id.startsWith(this.pathPrefix());
|
|
94
|
+
}
|
|
95
|
+
async goto(addr) {
|
|
96
|
+
if (addr.id.startsWith(this.pathPrefix())) {
|
|
97
|
+
const parsed = parseActionPath(addr.id, this.pathPrefix());
|
|
98
|
+
await this.relaunch();
|
|
99
|
+
for (const action of parsed.actions) {
|
|
100
|
+
await this.runner.perform(action).catch((error) => {
|
|
101
|
+
this.signals.push(desktopSignal("frozen", parsed.base, `desktop replay action failed: ${errMessage(error)}`, action.kind));
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
this.anchor = { id: addr.id, scope: addr.scope, ...(addr.label !== undefined ? { label: addr.label } : {}) };
|
|
105
|
+
this.baseAnchorId = parsed.base;
|
|
106
|
+
this.currentActions = parsed.actions;
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
await this.relaunch();
|
|
110
|
+
this.anchor = { id: LAUNCH_ID, scope: LAUNCH_ID, label: "launch" };
|
|
111
|
+
this.baseAnchorId = LAUNCH_ID;
|
|
112
|
+
this.currentActions = [];
|
|
113
|
+
}
|
|
114
|
+
this.drifted = false;
|
|
115
|
+
this.justWentTo = true;
|
|
116
|
+
this.anchorFp = "";
|
|
117
|
+
}
|
|
118
|
+
async relaunch() {
|
|
119
|
+
try {
|
|
120
|
+
await this.runner.launch(this.launchTarget);
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
this.signals.push(desktopSignal("crash", this.anchor.scope, `desktop launch failed: ${errMessage(error)}`, "launch"));
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
async snapshot() {
|
|
127
|
+
let tree = null;
|
|
128
|
+
try {
|
|
129
|
+
tree = await this.runner.snapshot();
|
|
130
|
+
}
|
|
131
|
+
catch (error) {
|
|
132
|
+
this.signals.push(desktopSignal("frozen", this.anchor.scope, `${this.automationLabel()} snapshot failed: ${errMessage(error)}`));
|
|
133
|
+
}
|
|
134
|
+
if (!tree) {
|
|
135
|
+
const details = this.takeDesktopEvidenceGaps(this.anchor.scope);
|
|
136
|
+
details.unshift({
|
|
137
|
+
kind: this.platform === "macos" ? "desktop-accessibility-unavailable" : "desktop-automation-unavailable",
|
|
138
|
+
severity: "blocker-for-exhaustive",
|
|
139
|
+
scope: this.anchor.scope,
|
|
140
|
+
message: `${this.automationLabel()} tree was unavailable`,
|
|
141
|
+
});
|
|
142
|
+
return {
|
|
143
|
+
address: { ...this.anchor },
|
|
144
|
+
title: this.anchor.label,
|
|
145
|
+
nodes: [],
|
|
146
|
+
blindSpots: details.length,
|
|
147
|
+
blindSpotDetails: details,
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
tree = await this.persistAutomationTree(tree);
|
|
151
|
+
this.lastTree = tree;
|
|
152
|
+
if (tree.artifactPath) {
|
|
153
|
+
this.observedAutomationTreeArtifacts.add(tree.artifactPath);
|
|
154
|
+
}
|
|
155
|
+
if (this.justWentTo) {
|
|
156
|
+
this.anchorFp = this.screenSignature(tree);
|
|
157
|
+
this.justWentTo = false;
|
|
158
|
+
}
|
|
159
|
+
const label = this.screenNameOf(tree, this.anchor.label ?? this.anchor.scope);
|
|
160
|
+
const address = { id: this.anchor.id, scope: this.anchor.scope, label };
|
|
161
|
+
this.currentScope = address.scope;
|
|
162
|
+
const blindSpotDetails = [...this.blindSpotDetailsOf(tree, address.scope), ...this.takeDesktopEvidenceGaps(address.scope)];
|
|
163
|
+
return {
|
|
164
|
+
address,
|
|
165
|
+
title: label,
|
|
166
|
+
nodes: this.surfaceNodesOf(tree),
|
|
167
|
+
blindSpots: blindSpotDetails.length,
|
|
168
|
+
blindSpotDetails,
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
async exercise(node) {
|
|
172
|
+
const action = this.actionForNode(node);
|
|
173
|
+
const scope = this.currentScope;
|
|
174
|
+
const signals = [];
|
|
175
|
+
try {
|
|
176
|
+
await this.runner.perform(action);
|
|
177
|
+
this.controlsExercised++;
|
|
178
|
+
this.cov.bumpExtra("desktopControlsExercised");
|
|
179
|
+
}
|
|
180
|
+
catch (error) {
|
|
181
|
+
this.drifted = true;
|
|
182
|
+
signals.push(desktopSignal("frozen", scope, `${node.kind} "${node.label}" did not respond: ${errMessage(error)}`, node.label, node.id));
|
|
183
|
+
return { signals };
|
|
184
|
+
}
|
|
185
|
+
const foreground = await this.runner.foregroundProcess().catch(() => expectedProcessName(this.launchTarget));
|
|
186
|
+
const expectedProcess = expectedProcessName(this.launchTarget);
|
|
187
|
+
if (expectedProcess && foreground && normalizeProcessName(foreground) !== normalizeProcessName(expectedProcess)) {
|
|
188
|
+
this.drifted = true;
|
|
189
|
+
signals.push(desktopSignal("crash", scope, `desktop app left foreground after "${node.label}" (now: ${foreground})`, node.label, node.id));
|
|
190
|
+
}
|
|
191
|
+
let after = null;
|
|
192
|
+
try {
|
|
193
|
+
after = await this.runner.snapshot();
|
|
194
|
+
}
|
|
195
|
+
catch (error) {
|
|
196
|
+
this.drifted = true;
|
|
197
|
+
signals.push(desktopSignal("frozen", scope, `screen unresponsive after "${node.label}": ${errMessage(error)}`, node.label, node.id));
|
|
198
|
+
}
|
|
199
|
+
if (after) {
|
|
200
|
+
if (after.artifactPath) {
|
|
201
|
+
this.observedAutomationTreeArtifacts.add(after.artifactPath);
|
|
202
|
+
}
|
|
203
|
+
const afterFp = this.screenSignature(after);
|
|
204
|
+
if (this.anchorFp && afterFp !== this.anchorFp) {
|
|
205
|
+
this.drifted = true;
|
|
206
|
+
return { signals, discovered: [this.actionPathAddress(after, [...this.currentActions, action])] };
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
return { signals };
|
|
210
|
+
}
|
|
211
|
+
async probe(phase) {
|
|
212
|
+
if (phase === "session") {
|
|
213
|
+
const signals = [];
|
|
214
|
+
const expectedProcess = expectedProcessName(this.launchTarget);
|
|
215
|
+
const foreground = await this.runner.foregroundProcess().catch(() => "");
|
|
216
|
+
if (expectedProcess && foreground && normalizeProcessName(foreground) !== normalizeProcessName(expectedProcess)) {
|
|
217
|
+
signals.push(desktopSignal("crash", this.anchor.scope, `desktop app not foreground after launch (now: ${foreground})`, "launch"));
|
|
218
|
+
}
|
|
219
|
+
try {
|
|
220
|
+
await this.runner.snapshot();
|
|
221
|
+
}
|
|
222
|
+
catch (error) {
|
|
223
|
+
signals.push(desktopSignal("frozen", this.anchor.scope, `${this.automationLabel()} tree unavailable: ${errMessage(error)}`, "snapshot"));
|
|
224
|
+
}
|
|
225
|
+
return signals;
|
|
226
|
+
}
|
|
227
|
+
if (phase === "dom") {
|
|
228
|
+
return this.lastTree ? firstErrorText(this.lastTree, this.currentScope) : [];
|
|
229
|
+
}
|
|
230
|
+
if (phase === "crash") {
|
|
231
|
+
const expectedProcess = expectedProcessName(this.launchTarget);
|
|
232
|
+
const foreground = await this.runner.foregroundProcess().catch(() => expectedProcess);
|
|
233
|
+
return expectedProcess && foreground && normalizeProcessName(foreground) !== normalizeProcessName(expectedProcess)
|
|
234
|
+
? [desktopSignal("crash", this.currentScope, `desktop app not foreground (now: ${foreground})`, "after-exercise")]
|
|
235
|
+
: [];
|
|
236
|
+
}
|
|
237
|
+
return [];
|
|
238
|
+
}
|
|
239
|
+
async softReset() {
|
|
240
|
+
/* Windows UIA v1 has no generic, safe reset key. The core escalates to goto() when needed. */
|
|
241
|
+
}
|
|
242
|
+
drainSignals() {
|
|
243
|
+
const out = this.signals;
|
|
244
|
+
this.signals = [];
|
|
245
|
+
return out;
|
|
246
|
+
}
|
|
247
|
+
async screenshot() {
|
|
248
|
+
return (await this.runner.screenshot?.().catch(() => Buffer.alloc(0))) ?? Buffer.alloc(0);
|
|
249
|
+
}
|
|
250
|
+
auditExtra() {
|
|
251
|
+
return {
|
|
252
|
+
desktopAudit: {
|
|
253
|
+
platform: this.platform,
|
|
254
|
+
automationBackend: this.automationBackend(),
|
|
255
|
+
appId: this.launchTarget.appId,
|
|
256
|
+
appPath: this.launchTarget.appPath,
|
|
257
|
+
processName: this.launchTarget.processName,
|
|
258
|
+
controlsExercised: this.controlsExercised,
|
|
259
|
+
egressEnforced: hasCompleteDesktopEgressProxyEvidence(this.currentDesktopEvidence().egressProxy),
|
|
260
|
+
},
|
|
261
|
+
desktopEvidence: this.currentDesktopEvidence(),
|
|
262
|
+
desktopReadiness: this.currentDesktopReadiness(),
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
actionPathAddress(after, actions) {
|
|
266
|
+
const fingerprint = this.screenSignature(after);
|
|
267
|
+
const label = this.screenNameOf(after, "");
|
|
268
|
+
const scope = label ? label.toLowerCase().replace(/[^a-z0-9/_-]+/g, "-") : `desktop/${fingerprint.slice(0, 8)}`;
|
|
269
|
+
return {
|
|
270
|
+
id: encodeActionPath(this.pathPrefix(), this.baseAnchorId, actions),
|
|
271
|
+
scope,
|
|
272
|
+
dedupeKey: `${this.platform}:${fingerprint}`,
|
|
273
|
+
label: label || scope,
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
takeDesktopEvidenceGaps(scope) {
|
|
277
|
+
if (this.desktopEvidenceGapsReported)
|
|
278
|
+
return [];
|
|
279
|
+
this.desktopEvidenceGapsReported = true;
|
|
280
|
+
return this.currentDesktopEvidence().gaps.map((gap) => ({ ...gap, scope }));
|
|
281
|
+
}
|
|
282
|
+
currentDesktopEvidence() {
|
|
283
|
+
return buildDesktopEvidence(this.currentDesktopProfile());
|
|
284
|
+
}
|
|
285
|
+
currentDesktopReadiness() {
|
|
286
|
+
return evaluateDesktopReadiness(this.currentDesktopProfile());
|
|
287
|
+
}
|
|
288
|
+
currentDesktopProfile() {
|
|
289
|
+
const existingArtifacts = this.desktopProfile.evidenceArtifacts ?? [];
|
|
290
|
+
const artifactPaths = new Set(existingArtifacts.map((artifact) => `${artifact.kind}:${artifact.path}`));
|
|
291
|
+
const evidenceArtifacts = existingArtifacts.map((artifact) => ({ ...artifact }));
|
|
292
|
+
for (const path of this.observedAutomationTreeArtifacts) {
|
|
293
|
+
const kind = this.platform === "macos" ? "accessibility-tree" : "automation-tree";
|
|
294
|
+
const key = `${kind}:${path}`;
|
|
295
|
+
if (artifactPaths.has(key))
|
|
296
|
+
continue;
|
|
297
|
+
evidenceArtifacts.push({ kind, path });
|
|
298
|
+
}
|
|
299
|
+
return {
|
|
300
|
+
...this.desktopProfile,
|
|
301
|
+
platform: this.platform,
|
|
302
|
+
automationBackend: this.automationBackend(),
|
|
303
|
+
...(this.platform === "windows"
|
|
304
|
+
? { uiAutomationTreeAvailable: this.observedAutomationTreeArtifacts.size > 0 || this.desktopProfile.uiAutomationTreeAvailable === true }
|
|
305
|
+
: { accessibilityPermissionVerified: this.observedAutomationTreeArtifacts.size > 0 || this.desktopProfile.accessibilityPermissionVerified === true }),
|
|
306
|
+
evidenceArtifacts,
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
async persistAutomationTree(tree) {
|
|
310
|
+
if (tree.artifactPath || this.runDir === undefined)
|
|
311
|
+
return tree;
|
|
312
|
+
this.snapshotArtifactCount++;
|
|
313
|
+
const prefix = this.platform === "macos" ? "desktop-ax-state" : "desktop-uia-state";
|
|
314
|
+
const artifactPath = join(this.runDir, `${prefix}-${this.snapshotArtifactCount}.json`);
|
|
315
|
+
try {
|
|
316
|
+
await writeFile(artifactPath, JSON.stringify(tree, null, 2), "utf8");
|
|
317
|
+
return { ...tree, artifactPath };
|
|
318
|
+
}
|
|
319
|
+
catch (error) {
|
|
320
|
+
this.signals.push(desktopSignal("desktop-evidence", this.anchor.scope, `failed to write desktop tree artifact: ${errMessage(error)}`));
|
|
321
|
+
return tree;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
pathPrefix() {
|
|
325
|
+
return this.platform === "macos" ? AX_PATH_PREFIX : UIA_PATH_PREFIX;
|
|
326
|
+
}
|
|
327
|
+
automationBackend() {
|
|
328
|
+
return this.platform === "macos" ? "accessibility" : "uia";
|
|
329
|
+
}
|
|
330
|
+
automationLabel() {
|
|
331
|
+
return this.platform === "macos" ? "macOS Accessibility" : "Windows UI Automation";
|
|
332
|
+
}
|
|
333
|
+
surfaceNodesOf(tree) {
|
|
334
|
+
return this.platform === "macos"
|
|
335
|
+
? macosAccessibilityViewToSurfaceNodes(tree)
|
|
336
|
+
: viewToSurfaceNodes(tree);
|
|
337
|
+
}
|
|
338
|
+
screenNameOf(tree, fallback) {
|
|
339
|
+
return this.platform === "macos"
|
|
340
|
+
? macosAccessibilityScreenNameOf(tree, fallback)
|
|
341
|
+
: windowsScreenNameOf(tree, fallback);
|
|
342
|
+
}
|
|
343
|
+
screenSignature(tree) {
|
|
344
|
+
return this.platform === "macos"
|
|
345
|
+
? macosAccessibilityScreenSignature(tree)
|
|
346
|
+
: windowsScreenSignature(tree);
|
|
347
|
+
}
|
|
348
|
+
blindSpotDetailsOf(tree, scope) {
|
|
349
|
+
return this.platform === "macos"
|
|
350
|
+
? macosAccessibilityBlindSpotDetailsOf(tree, scope)
|
|
351
|
+
: blindSpotDetailsOf(tree, scope);
|
|
352
|
+
}
|
|
353
|
+
actionForNode(node) {
|
|
354
|
+
if (this.platform === "macos") {
|
|
355
|
+
return macosActionForHandle(node.handle);
|
|
356
|
+
}
|
|
357
|
+
return windowsActionForHandle(node.handle);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
function windowsActionForHandle(handle) {
|
|
361
|
+
if (handle.actionKind === "set-value") {
|
|
362
|
+
return { kind: "set-value", selector: handle.selector, value: PROBE_TEXT };
|
|
363
|
+
}
|
|
364
|
+
if (handle.actionKind === "range") {
|
|
365
|
+
return { kind: "range", selector: handle.selector, delta: 1 };
|
|
366
|
+
}
|
|
367
|
+
return { kind: handle.actionKind, selector: handle.selector };
|
|
368
|
+
}
|
|
369
|
+
function macosActionForHandle(handle) {
|
|
370
|
+
if (handle.actionKind === "set-value") {
|
|
371
|
+
return { kind: "set-value", selector: handle.selector, value: PROBE_TEXT };
|
|
372
|
+
}
|
|
373
|
+
if (handle.actionKind === "increment" || handle.actionKind === "decrement") {
|
|
374
|
+
return { kind: handle.actionKind, selector: handle.selector, delta: 1 };
|
|
375
|
+
}
|
|
376
|
+
return { kind: handle.actionKind, selector: handle.selector };
|
|
377
|
+
}
|
|
378
|
+
function hasCompleteDesktopEgressProxyEvidence(proxy) {
|
|
379
|
+
return proxy?.verified === true && Boolean(proxy.provider && proxy.command && proxy.artifactPath);
|
|
380
|
+
}
|
|
381
|
+
function expectedProcessName(target) {
|
|
382
|
+
if (target.processId !== undefined)
|
|
383
|
+
return "";
|
|
384
|
+
const processName = target.processName ?? target.appPath?.split(/[\\/]/).pop() ?? "";
|
|
385
|
+
return processName.replace(/\.exe$/i, "").replace(/\.app$/i, "");
|
|
386
|
+
}
|
|
387
|
+
function normalizeProcessName(value) {
|
|
388
|
+
return value.replace(/\.exe$/i, "").toLowerCase();
|
|
389
|
+
}
|
|
390
|
+
function encodeActionPath(prefix, base, actions) {
|
|
391
|
+
const tokens = actions.map((action) => Buffer.from(JSON.stringify(action), "utf8").toString("base64url"));
|
|
392
|
+
return `${prefix}${base}${BASE_SEP}${tokens.join(ACTION_SEP)}`;
|
|
393
|
+
}
|
|
394
|
+
function parseActionPath(id, prefix) {
|
|
395
|
+
const body = id.slice(prefix.length);
|
|
396
|
+
const sep = body.indexOf(BASE_SEP);
|
|
397
|
+
const base = sep < 0 ? body : body.slice(0, sep);
|
|
398
|
+
const actionText = sep < 0 ? "" : body.slice(sep + BASE_SEP.length);
|
|
399
|
+
const actions = actionText
|
|
400
|
+
? actionText.split(ACTION_SEP).map((token) => JSON.parse(Buffer.from(token, "base64url").toString("utf8")))
|
|
401
|
+
: [];
|
|
402
|
+
return { base, actions };
|
|
403
|
+
}
|
|
404
|
+
function errMessage(error) {
|
|
405
|
+
return (error instanceof Error ? error.message : String(error)).slice(0, 160);
|
|
406
|
+
}
|
|
407
|
+
function desktopSignal(kind, scope, message, action, discriminator) {
|
|
408
|
+
return {
|
|
409
|
+
kind,
|
|
410
|
+
severity: kind === "crash" ? "fail" : "warn",
|
|
411
|
+
message,
|
|
412
|
+
url: scope,
|
|
413
|
+
signature: `desktop:${kind}:${scope}:${action ?? ""}:${discriminator ?? ""}:${message.slice(0, 80)}`,
|
|
414
|
+
...(action !== undefined ? { action } : {}),
|
|
415
|
+
};
|
|
416
|
+
}
|
|
417
|
+
function isWindowsUiaNode(node) {
|
|
418
|
+
return "controlType" in node || "automationId" in node || "name" in node;
|
|
419
|
+
}
|
|
420
|
+
function firstErrorText(tree, scope) {
|
|
421
|
+
const stack = [tree.root];
|
|
422
|
+
while (stack.length > 0) {
|
|
423
|
+
const node = stack.pop();
|
|
424
|
+
const text = isWindowsUiaNode(node) ? (node.name ?? "").trim() : `${node.title ?? ""} ${node.value ?? ""}`.trim();
|
|
425
|
+
if (/\b(error|failed|exception|not responding|crash|fatal)\b/i.test(text)) {
|
|
426
|
+
return [desktopSignal("error-text", scope, `on-screen desktop error: ${text.slice(0, 120)}`)];
|
|
427
|
+
}
|
|
428
|
+
for (const child of node.children ?? []) {
|
|
429
|
+
stack.push(child);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
return [];
|
|
433
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"driver.js","sourceRoot":"","sources":["../../src/desktop/driver.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import { normalizeDesktopProfile } from "./desktop-profile.js";
|
|
2
|
+
const VALID_ARTIFACT_KINDS = new Set([
|
|
3
|
+
"screenshot",
|
|
4
|
+
"video",
|
|
5
|
+
"log",
|
|
6
|
+
"crash-log",
|
|
7
|
+
"accessibility-tree",
|
|
8
|
+
"automation-tree",
|
|
9
|
+
]);
|
|
10
|
+
const VALID_AUTOMATION_BACKENDS = new Set([
|
|
11
|
+
"accessibility",
|
|
12
|
+
"xctest",
|
|
13
|
+
"uia",
|
|
14
|
+
"unknown",
|
|
15
|
+
]);
|
|
16
|
+
function isRecord(value) {
|
|
17
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
18
|
+
}
|
|
19
|
+
function isNonEmptyString(value) {
|
|
20
|
+
return typeof value === "string" && value.trim().length > 0;
|
|
21
|
+
}
|
|
22
|
+
function hasAppIdentity(profile) {
|
|
23
|
+
return isNonEmptyString(profile.appId) || isNonEmptyString(profile.appPath) || isNonEmptyString(profile.processName);
|
|
24
|
+
}
|
|
25
|
+
function isPlatform(value) {
|
|
26
|
+
return value === "macos" || value === "windows";
|
|
27
|
+
}
|
|
28
|
+
function isAutomationBackend(value) {
|
|
29
|
+
return typeof value === "string" && VALID_AUTOMATION_BACKENDS.has(value);
|
|
30
|
+
}
|
|
31
|
+
function isArtifactKind(value) {
|
|
32
|
+
return typeof value === "string" && VALID_ARTIFACT_KINDS.has(value);
|
|
33
|
+
}
|
|
34
|
+
function hasPathTraversal(value) {
|
|
35
|
+
return value.replace(/\\/g, "/").split("/").includes("..");
|
|
36
|
+
}
|
|
37
|
+
function addValidationIssue(errors, code, field, message) {
|
|
38
|
+
errors.push({ code, field, message });
|
|
39
|
+
}
|
|
40
|
+
function validateEvidenceArtifacts(errors, artifactsValue) {
|
|
41
|
+
const artifacts = Array.isArray(artifactsValue) ? artifactsValue : [];
|
|
42
|
+
for (let index = 0; index < artifacts.length; index++) {
|
|
43
|
+
const artifact = artifacts[index];
|
|
44
|
+
const field = `evidenceArtifacts[${index}]`;
|
|
45
|
+
if (!isRecord(artifact)) {
|
|
46
|
+
addValidationIssue(errors, "evidence-artifact-kind-invalid", `${field}.kind`, "Evidence artifact must be an object");
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
if (!isArtifactKind(artifact.kind)) {
|
|
50
|
+
addValidationIssue(errors, "evidence-artifact-kind-invalid", `${field}.kind`, "Evidence artifact kind must be screenshot, video, log, crash-log, accessibility-tree, or automation-tree");
|
|
51
|
+
}
|
|
52
|
+
if (!isNonEmptyString(artifact.path)) {
|
|
53
|
+
addValidationIssue(errors, "evidence-artifact-path-missing", `${field}.path`, "Evidence artifact path must be non-empty");
|
|
54
|
+
}
|
|
55
|
+
else if (hasPathTraversal(artifact.path)) {
|
|
56
|
+
addValidationIssue(errors, "evidence-artifact-path-traversal", `${field}.path`, "Evidence artifact path must not contain '..'");
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
function validateEgressProxy(errors, proxy) {
|
|
61
|
+
if (proxy?.verified !== true)
|
|
62
|
+
return;
|
|
63
|
+
if (!isNonEmptyString(proxy.provider)) {
|
|
64
|
+
addValidationIssue(errors, "egress-proxy-provider-missing", "egressProxy.provider", "Verified proxy evidence requires a provider");
|
|
65
|
+
}
|
|
66
|
+
if (!isNonEmptyString(proxy.command)) {
|
|
67
|
+
addValidationIssue(errors, "egress-proxy-command-missing", "egressProxy.command", "Verified proxy evidence requires the proxy command");
|
|
68
|
+
}
|
|
69
|
+
if (!isNonEmptyString(proxy.artifactPath)) {
|
|
70
|
+
addValidationIssue(errors, "egress-proxy-artifact-missing", "egressProxy.artifactPath", "Verified proxy evidence requires an artifact path");
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
export function validateDesktopAppProfile(input) {
|
|
74
|
+
const profile = normalizeDesktopProfile(input);
|
|
75
|
+
const errors = [];
|
|
76
|
+
if (!hasAppIdentity(profile)) {
|
|
77
|
+
addValidationIssue(errors, "app-identity-missing", "appId", "Desktop evidence requires an app id, path, or process name");
|
|
78
|
+
}
|
|
79
|
+
if (!isPlatform(profile.platform)) {
|
|
80
|
+
addValidationIssue(errors, "platform-invalid", "platform", "Desktop evidence platform must be macos or windows");
|
|
81
|
+
}
|
|
82
|
+
if (!isAutomationBackend(profile.automationBackend)) {
|
|
83
|
+
addValidationIssue(errors, "automation-backend-invalid", "automationBackend", "Desktop automation backend must be accessibility, xctest, uia, or unknown");
|
|
84
|
+
}
|
|
85
|
+
validateEvidenceArtifacts(errors, profile.evidenceArtifacts);
|
|
86
|
+
if (profile.providerRunId !== undefined && !isNonEmptyString(profile.provider)) {
|
|
87
|
+
addValidationIssue(errors, "provider-required-for-run-id", "provider", "Provider run id requires provider metadata");
|
|
88
|
+
}
|
|
89
|
+
validateEgressProxy(errors, profile.egressProxy);
|
|
90
|
+
return { errors };
|
|
91
|
+
}
|
|
92
|
+
function validEvidenceArtifacts(profile) {
|
|
93
|
+
const artifacts = Array.isArray(profile.evidenceArtifacts) ? profile.evidenceArtifacts : [];
|
|
94
|
+
const out = [];
|
|
95
|
+
for (const artifact of artifacts) {
|
|
96
|
+
if (!isRecord(artifact))
|
|
97
|
+
continue;
|
|
98
|
+
if (!isArtifactKind(artifact.kind))
|
|
99
|
+
continue;
|
|
100
|
+
if (!isNonEmptyString(artifact.path))
|
|
101
|
+
continue;
|
|
102
|
+
if (hasPathTraversal(artifact.path))
|
|
103
|
+
continue;
|
|
104
|
+
out.push({ kind: artifact.kind, path: artifact.path });
|
|
105
|
+
}
|
|
106
|
+
return out;
|
|
107
|
+
}
|
|
108
|
+
function pathsForKind(artifacts, kind) {
|
|
109
|
+
const out = [];
|
|
110
|
+
for (const artifact of artifacts) {
|
|
111
|
+
if (artifact.kind === kind)
|
|
112
|
+
out.push(artifact.path);
|
|
113
|
+
}
|
|
114
|
+
return out;
|
|
115
|
+
}
|
|
116
|
+
function hasCompleteDesktopEgressProxyEvidence(proxy) {
|
|
117
|
+
return proxy?.verified === true && Boolean(proxy.provider && proxy.command && proxy.artifactPath);
|
|
118
|
+
}
|
|
119
|
+
function desktopEgressGap(profile) {
|
|
120
|
+
if (hasCompleteDesktopEgressProxyEvidence(profile.egressProxy))
|
|
121
|
+
return undefined;
|
|
122
|
+
if (profile.egressProxy?.verified === true) {
|
|
123
|
+
return {
|
|
124
|
+
kind: "desktop-egress-unenforced",
|
|
125
|
+
severity: "blocker-for-exhaustive",
|
|
126
|
+
scope: profile.platform,
|
|
127
|
+
message: "Desktop network egress proxy was marked verified, but verified proxy evidence is incomplete; provide provider, command, and artifact",
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
return {
|
|
131
|
+
kind: "desktop-egress-unenforced",
|
|
132
|
+
severity: "blocker-for-exhaustive",
|
|
133
|
+
scope: profile.platform,
|
|
134
|
+
message: "Desktop network egress is not enforced without verified OS-level proxy evidence",
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
function platformEvidenceGaps(profile, artifacts) {
|
|
138
|
+
const gaps = [];
|
|
139
|
+
if (profile.platform === "macos") {
|
|
140
|
+
const hasAccessibilityTree = pathsForKind(artifacts, "accessibility-tree").length > 0;
|
|
141
|
+
if (profile.accessibilityPermissionVerified !== true || !hasAccessibilityTree) {
|
|
142
|
+
gaps.push({
|
|
143
|
+
kind: "desktop-accessibility-unavailable",
|
|
144
|
+
severity: "blocker-for-exhaustive",
|
|
145
|
+
scope: "macos",
|
|
146
|
+
message: "macOS desktop coverage requires verified Accessibility permission and an accessibility-tree artifact",
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
if (profile.platform === "windows") {
|
|
151
|
+
const hasAutomationTree = profile.uiAutomationTreeAvailable === true || pathsForKind(artifacts, "automation-tree").length > 0;
|
|
152
|
+
if (!hasAutomationTree) {
|
|
153
|
+
gaps.push({
|
|
154
|
+
kind: "desktop-automation-unavailable",
|
|
155
|
+
severity: "blocker-for-exhaustive",
|
|
156
|
+
scope: "windows",
|
|
157
|
+
message: "Windows desktop coverage requires UI Automation tree evidence",
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return gaps;
|
|
162
|
+
}
|
|
163
|
+
export function buildDesktopEvidence(input) {
|
|
164
|
+
const profile = normalizeDesktopProfile(input);
|
|
165
|
+
const validation = validateDesktopAppProfile(profile);
|
|
166
|
+
const evidenceArtifacts = validEvidenceArtifacts(profile);
|
|
167
|
+
const egressGap = desktopEgressGap(profile);
|
|
168
|
+
const validationGaps = validation.errors.length > 0
|
|
169
|
+
? [
|
|
170
|
+
{
|
|
171
|
+
kind: "desktop-evidence-invalid",
|
|
172
|
+
severity: "blocker-for-exhaustive",
|
|
173
|
+
scope: profile.platform,
|
|
174
|
+
message: `Desktop evidence validation failed: ${validation.errors.map((error) => error.code).join(", ")}`,
|
|
175
|
+
},
|
|
176
|
+
]
|
|
177
|
+
: [];
|
|
178
|
+
const gaps = [
|
|
179
|
+
...(egressGap ? [egressGap] : []),
|
|
180
|
+
...validationGaps,
|
|
181
|
+
...platformEvidenceGaps(profile, evidenceArtifacts),
|
|
182
|
+
];
|
|
183
|
+
return {
|
|
184
|
+
platform: profile.platform,
|
|
185
|
+
...(profile.appId !== undefined ? { appId: profile.appId } : {}),
|
|
186
|
+
...(profile.appPath !== undefined ? { appPath: profile.appPath } : {}),
|
|
187
|
+
...(profile.processName !== undefined ? { processName: profile.processName } : {}),
|
|
188
|
+
...(profile.appVersion !== undefined ? { appVersion: profile.appVersion } : {}),
|
|
189
|
+
...(profile.osVersion !== undefined ? { osVersion: profile.osVersion } : {}),
|
|
190
|
+
automationBackend: profile.automationBackend ?? "unknown",
|
|
191
|
+
...(profile.provider !== undefined ? { provider: profile.provider } : {}),
|
|
192
|
+
...(profile.providerRunId !== undefined ? { providerRunId: profile.providerRunId } : {}),
|
|
193
|
+
...(profile.launchCommand !== undefined ? { launchCommand: profile.launchCommand } : {}),
|
|
194
|
+
...(profile.verificationCommand !== undefined ? { verificationCommand: profile.verificationCommand } : {}),
|
|
195
|
+
evidenceArtifacts: evidenceArtifacts.map((artifact) => ({ ...artifact })),
|
|
196
|
+
screenshots: pathsForKind(evidenceArtifacts, "screenshot"),
|
|
197
|
+
videos: pathsForKind(evidenceArtifacts, "video"),
|
|
198
|
+
logs: pathsForKind(evidenceArtifacts, "log"),
|
|
199
|
+
crashLogs: pathsForKind(evidenceArtifacts, "crash-log"),
|
|
200
|
+
accessibilityTrees: pathsForKind(evidenceArtifacts, "accessibility-tree"),
|
|
201
|
+
automationTrees: pathsForKind(evidenceArtifacts, "automation-tree"),
|
|
202
|
+
...(profile.egressProxy !== undefined ? { egressProxy: { ...profile.egressProxy } } : {}),
|
|
203
|
+
validationErrors: validation.errors.map((error) => ({ ...error })),
|
|
204
|
+
gaps,
|
|
205
|
+
};
|
|
206
|
+
}
|