Add explicit Chrome debug sweep
This commit is contained in:
parent
216d4e6fea
commit
fa3b9c7778
9 changed files with 320 additions and 8 deletions
11
README.md
11
README.md
|
|
@ -41,8 +41,14 @@ Then install the extension:
|
|||
4. Select `E:\Projects\Loki\extension`.
|
||||
|
||||
The extension posts tab metadata to
|
||||
`http://127.0.0.1:17666/ingest/chrome-extension`. It does not read page DOM,
|
||||
cookies, or page content.
|
||||
`http://127.0.0.1:17666/ingest/chrome-extension`. Its normal sync does not read
|
||||
page DOM, cookies, or page content.
|
||||
|
||||
The popup also exposes `Debug sweep`. Chrome will warn that Loki can debug
|
||||
pages because the extension uses `chrome.debugger`. A sweep attaches to each
|
||||
debuggable tab, probes Chrome Debugger Protocol domains, records frame/runtime/
|
||||
performance/security/root-DOM metadata, then detaches. It does not read response
|
||||
bodies, cookies, form values, or full page DOM.
|
||||
|
||||
## CDP Observation
|
||||
|
||||
|
|
@ -65,6 +71,7 @@ npm run smoke
|
|||
Loki writes:
|
||||
|
||||
- `state/loki.chrome_snapshot.cc`
|
||||
- `state/loki.chrome_debug_probe.cc`
|
||||
- `state/loki.provider_advertisement.cc`
|
||||
- `state/loki.eve_surface.cc`
|
||||
|
||||
|
|
|
|||
|
|
@ -8,20 +8,24 @@ CultMesh/Eve consumers while keeping Chrome installation easy for the operator.
|
|||
## Current Mechanism
|
||||
|
||||
The Chrome extension observes tab metadata with explicit Chrome permissions and
|
||||
posts reports to Loki's localhost ingest endpoint. Chrome can also expose
|
||||
DevTools metadata on `127.0.0.1:{port}` when started with
|
||||
`--remote-debugging-port`. Loki normalizes both sources into one snapshot and
|
||||
writes three documents from that snapshot.
|
||||
posts reports to Loki's localhost ingest endpoint. On command, it also uses
|
||||
`chrome.debugger` to attach to debuggable tabs, probe Chrome Debugger Protocol
|
||||
domains, and detach. Chrome can also expose DevTools metadata on
|
||||
`127.0.0.1:{port}` when started with `--remote-debugging-port`. Loki normalizes
|
||||
these sources into one snapshot and writes typed witness documents from that
|
||||
state.
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Extension["Chrome extension"] --> Ingest["extension ingest"]
|
||||
Debugger["chrome.debugger sweep"] --> Ingest
|
||||
Chrome["Chrome CDP endpoint"] --> Observe["observe CDP"]
|
||||
Config["Loki config"] --> Ingest
|
||||
Config --> Observe
|
||||
Ingest --> Combine["combined snapshot"]
|
||||
Observe --> Combine
|
||||
Combine --> Snapshot["chrome_snapshot.cc"]
|
||||
Combine --> Debug["chrome_debug_probe.cc"]
|
||||
Snapshot --> Provider["provider_advertisement.cc"]
|
||||
Snapshot --> Surface["eve_surface.cc"]
|
||||
```
|
||||
|
|
@ -30,6 +34,7 @@ flowchart LR
|
|||
|
||||
- CDP reachability is observed state, not assumed capability.
|
||||
- Extension reachability is observed state, not assumed capability.
|
||||
- Debug sweeps are explicit observations, not continuous hidden attachment.
|
||||
- Chrome owns page reality; Loki owns only the observation snapshot.
|
||||
- `.cc` witnesses are the durable local state surface.
|
||||
- Eve/CultUI output is derived from the same snapshot as the witness.
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
"description": "Publishes Chrome tab metadata to the local Loki CultMesh daemon.",
|
||||
"permissions": [
|
||||
"alarms",
|
||||
"debugger",
|
||||
"storage",
|
||||
"tabs"
|
||||
],
|
||||
|
|
|
|||
|
|
@ -85,6 +85,12 @@ button {
|
|||
cursor: pointer;
|
||||
}
|
||||
|
||||
button + button {
|
||||
margin-top: 8px;
|
||||
border-color: #34516f;
|
||||
background: #386181;
|
||||
}
|
||||
|
||||
button:disabled {
|
||||
cursor: wait;
|
||||
opacity: 0.65;
|
||||
|
|
|
|||
|
|
@ -26,11 +26,16 @@
|
|||
<dt>Endpoint</dt>
|
||||
<dd id="endpoint">-</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>Debug</dt>
|
||||
<dd id="debug">-</dd>
|
||||
</div>
|
||||
</dl>
|
||||
|
||||
<p id="error" class="error" hidden></p>
|
||||
|
||||
<button id="sync" type="button">Sync now</button>
|
||||
<button id="debug-sweep" type="button">Debug sweep</button>
|
||||
</main>
|
||||
<script src="popup.js" type="module"></script>
|
||||
</body>
|
||||
|
|
|
|||
|
|
@ -2,8 +2,10 @@ const stateEl = document.querySelector("#state");
|
|||
const tabsEl = document.querySelector("#tabs");
|
||||
const lastSyncEl = document.querySelector("#last-sync");
|
||||
const endpointEl = document.querySelector("#endpoint");
|
||||
const debugEl = document.querySelector("#debug");
|
||||
const errorEl = document.querySelector("#error");
|
||||
const syncButton = document.querySelector("#sync");
|
||||
const debugSweepButton = document.querySelector("#debug-sweep");
|
||||
|
||||
syncButton.addEventListener("click", async () => {
|
||||
syncButton.disabled = true;
|
||||
|
|
@ -12,6 +14,13 @@ syncButton.addEventListener("click", async () => {
|
|||
syncButton.disabled = false;
|
||||
});
|
||||
|
||||
debugSweepButton.addEventListener("click", async () => {
|
||||
debugSweepButton.disabled = true;
|
||||
await chrome.runtime.sendMessage({ type: "loki.debug-sweep" });
|
||||
await render();
|
||||
debugSweepButton.disabled = false;
|
||||
});
|
||||
|
||||
await render();
|
||||
|
||||
async function render() {
|
||||
|
|
@ -23,6 +32,7 @@ async function render() {
|
|||
stateEl.textContent = "waiting";
|
||||
stateEl.className = "state";
|
||||
tabsEl.textContent = "-";
|
||||
debugEl.textContent = "-";
|
||||
lastSyncEl.textContent = "-";
|
||||
errorEl.hidden = true;
|
||||
return;
|
||||
|
|
@ -31,6 +41,7 @@ async function render() {
|
|||
stateEl.textContent = status.ok ? "connected" : "offline";
|
||||
stateEl.className = `state ${status.ok ? "ok" : "bad"}`;
|
||||
tabsEl.textContent = String(status.tabCount ?? 0);
|
||||
debugEl.textContent = formatDebugProbe(status.debugProbe);
|
||||
lastSyncEl.textContent = status.lastSyncAt ? new Date(status.lastSyncAt).toLocaleTimeString() : "-";
|
||||
|
||||
if (status.error) {
|
||||
|
|
@ -40,3 +51,9 @@ async function render() {
|
|||
errorEl.hidden = true;
|
||||
}
|
||||
}
|
||||
|
||||
function formatDebugProbe(debugProbe) {
|
||||
if (!debugProbe) return "not run";
|
||||
const summary = debugProbe.summary ?? {};
|
||||
return `${summary.attachedTabCount ?? 0}/${summary.attemptedTabCount ?? 0} attached`;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,6 +31,10 @@ chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => {
|
|||
syncTabs("manual").then(sendResponse);
|
||||
return true;
|
||||
}
|
||||
if (message?.type === "loki.debug-sweep") {
|
||||
debugSweep().then(sendResponse);
|
||||
return true;
|
||||
}
|
||||
if (message?.type === "loki.get-status") {
|
||||
getStatus().then(sendResponse);
|
||||
return true;
|
||||
|
|
@ -42,7 +46,7 @@ chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => {
|
|||
return false;
|
||||
});
|
||||
|
||||
async function syncTabs(reason) {
|
||||
async function syncTabs(reason, extra = {}) {
|
||||
const statusStartedAt = new Date().toISOString();
|
||||
try {
|
||||
const endpoint = await getEndpoint();
|
||||
|
|
@ -62,7 +66,9 @@ async function syncTabs(reason) {
|
|||
pageContent: "not-read",
|
||||
cookies: "not-read",
|
||||
tabMetadata: "title-url-favicon-window-state",
|
||||
debugProbe: extra.debugProbe ? "runtime-frame-performance-security-metadata" : "not-run",
|
||||
},
|
||||
...extra,
|
||||
};
|
||||
|
||||
const response = await fetch(endpoint, {
|
||||
|
|
@ -82,6 +88,7 @@ async function syncTabs(reason) {
|
|||
reason,
|
||||
lastSyncAt: new Date().toISOString(),
|
||||
tabCount: tabs.length,
|
||||
debugProbe: responseBody.debugProbe ?? null,
|
||||
activeSource: responseBody.activeSource ?? "unknown",
|
||||
error: null,
|
||||
};
|
||||
|
|
@ -95,6 +102,7 @@ async function syncTabs(reason) {
|
|||
reason,
|
||||
lastSyncAt: new Date().toISOString(),
|
||||
tabCount: 0,
|
||||
debugProbe: null,
|
||||
activeSource: "none",
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
};
|
||||
|
|
@ -103,6 +111,142 @@ async function syncTabs(reason) {
|
|||
}
|
||||
}
|
||||
|
||||
async function debugSweep() {
|
||||
const startedAt = new Date().toISOString();
|
||||
const tabs = await chrome.tabs.query({});
|
||||
const targets = await chromeDebuggerGetTargets();
|
||||
const probe = {
|
||||
schema: "gamecult.loki.chrome_debug_probe.v0",
|
||||
startedAt,
|
||||
completedAt: null,
|
||||
protocolVersion: "1.3",
|
||||
targetCount: targets.length,
|
||||
targets: targets.map(projectDebugTarget),
|
||||
tabs: [],
|
||||
summary: {
|
||||
attemptedTabCount: 0,
|
||||
attachedTabCount: 0,
|
||||
failedTabCount: 0,
|
||||
commandCount: 0,
|
||||
commandFailureCount: 0,
|
||||
},
|
||||
redaction: {
|
||||
pageContent: "not-read",
|
||||
cookies: "not-read",
|
||||
responseBodies: "not-read",
|
||||
domTree: "root-node-only",
|
||||
runtimeEvaluation: "location-title-readyState-visibility-performance-navigator",
|
||||
},
|
||||
};
|
||||
|
||||
for (const tab of tabs) {
|
||||
if (!Number.isInteger(tab.id)) continue;
|
||||
probe.summary.attemptedTabCount += 1;
|
||||
const tabProbe = await probeTab(tab);
|
||||
probe.tabs.push(tabProbe);
|
||||
if (tabProbe.attached) probe.summary.attachedTabCount += 1;
|
||||
else probe.summary.failedTabCount += 1;
|
||||
probe.summary.commandCount += tabProbe.commands.length;
|
||||
probe.summary.commandFailureCount += tabProbe.commands.filter((command) => !command.ok).length;
|
||||
}
|
||||
|
||||
probe.completedAt = new Date().toISOString();
|
||||
return syncTabs("debug-sweep", { debugProbe: probe });
|
||||
}
|
||||
|
||||
async function probeTab(tab) {
|
||||
const target = { tabId: tab.id };
|
||||
const tabProbe = {
|
||||
tabId: tab.id,
|
||||
windowId: tab.windowId,
|
||||
title: tab.title ?? null,
|
||||
url: tab.url ?? null,
|
||||
attached: false,
|
||||
attachError: null,
|
||||
commands: [],
|
||||
frameTree: null,
|
||||
runtime: null,
|
||||
performanceMetrics: null,
|
||||
securityState: null,
|
||||
domRoot: null,
|
||||
};
|
||||
|
||||
try {
|
||||
await chromeDebuggerAttach(target, "1.3");
|
||||
tabProbe.attached = true;
|
||||
|
||||
await recordCommand(tabProbe, target, "Page.enable");
|
||||
await recordCommand(tabProbe, target, "Runtime.enable");
|
||||
await recordCommand(tabProbe, target, "Log.enable");
|
||||
await recordCommand(tabProbe, target, "Performance.enable");
|
||||
await recordCommand(tabProbe, target, "Security.enable");
|
||||
await recordCommand(tabProbe, target, "DOM.enable");
|
||||
|
||||
tabProbe.frameTree = await recordCommand(tabProbe, target, "Page.getFrameTree");
|
||||
tabProbe.runtime = await recordCommand(tabProbe, target, "Runtime.evaluate", {
|
||||
expression: `({
|
||||
href: location.href,
|
||||
title: document.title,
|
||||
readyState: document.readyState,
|
||||
visibilityState: document.visibilityState,
|
||||
referrer: document.referrer,
|
||||
origin: location.origin,
|
||||
navigation: performance.getEntriesByType("navigation").map((entry) => entry.toJSON?.() ?? {}),
|
||||
screen: { width: screen.width, height: screen.height, colorDepth: screen.colorDepth },
|
||||
navigator: {
|
||||
userAgent: navigator.userAgent,
|
||||
language: navigator.language,
|
||||
languages: navigator.languages,
|
||||
hardwareConcurrency: navigator.hardwareConcurrency,
|
||||
deviceMemory: navigator.deviceMemory ?? null,
|
||||
platform: navigator.platform,
|
||||
cookieEnabled: navigator.cookieEnabled
|
||||
}
|
||||
})`,
|
||||
returnByValue: true,
|
||||
awaitPromise: false,
|
||||
});
|
||||
tabProbe.performanceMetrics = await recordCommand(tabProbe, target, "Performance.getMetrics");
|
||||
tabProbe.securityState = await recordCommand(tabProbe, target, "Security.getSecurityState");
|
||||
tabProbe.domRoot = await recordCommand(tabProbe, target, "DOM.getDocument", { depth: 0, pierce: false });
|
||||
} catch (error) {
|
||||
tabProbe.attachError = error instanceof Error ? error.message : String(error);
|
||||
} finally {
|
||||
if (tabProbe.attached) {
|
||||
await chromeDebuggerDetach(target).catch(() => {});
|
||||
}
|
||||
}
|
||||
|
||||
return tabProbe;
|
||||
}
|
||||
|
||||
async function recordCommand(tabProbe, target, method, params = undefined) {
|
||||
try {
|
||||
const result = await chromeDebuggerSendCommand(target, method, params);
|
||||
tabProbe.commands.push({ method, ok: true });
|
||||
return result ?? {};
|
||||
} catch (error) {
|
||||
tabProbe.commands.push({
|
||||
method,
|
||||
ok: false,
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
});
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function projectDebugTarget(target) {
|
||||
return {
|
||||
id: target.id ?? null,
|
||||
tabId: target.tabId ?? null,
|
||||
type: target.type ?? null,
|
||||
title: target.title ?? null,
|
||||
url: target.url ?? null,
|
||||
attached: Boolean(target.attached),
|
||||
extensionId: target.extensionId ?? null,
|
||||
};
|
||||
}
|
||||
|
||||
function projectTab(tab) {
|
||||
return {
|
||||
id: tab.id,
|
||||
|
|
@ -128,6 +272,46 @@ async function getStatus() {
|
|||
};
|
||||
}
|
||||
|
||||
function chromeDebuggerGetTargets() {
|
||||
return new Promise((resolve, reject) => {
|
||||
chrome.debugger.getTargets((targets) => {
|
||||
const error = chrome.runtime.lastError;
|
||||
if (error) reject(new Error(error.message));
|
||||
else resolve(targets ?? []);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function chromeDebuggerAttach(target, protocolVersion) {
|
||||
return new Promise((resolve, reject) => {
|
||||
chrome.debugger.attach(target, protocolVersion, () => {
|
||||
const error = chrome.runtime.lastError;
|
||||
if (error) reject(new Error(error.message));
|
||||
else resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function chromeDebuggerSendCommand(target, method, params) {
|
||||
return new Promise((resolve, reject) => {
|
||||
chrome.debugger.sendCommand(target, method, params ?? {}, (result) => {
|
||||
const error = chrome.runtime.lastError;
|
||||
if (error) reject(new Error(error.message));
|
||||
else resolve(result);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function chromeDebuggerDetach(target) {
|
||||
return new Promise((resolve, reject) => {
|
||||
chrome.debugger.detach(target, () => {
|
||||
const error = chrome.runtime.lastError;
|
||||
if (error) reject(new Error(error.message));
|
||||
else resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function getEndpoint() {
|
||||
const values = await chrome.storage.local.get(CONFIG_KEY);
|
||||
return values[CONFIG_KEY]?.endpoint ?? DEFAULT_ENDPOINT;
|
||||
|
|
|
|||
|
|
@ -151,10 +151,21 @@ function normalizeExtensionObservation(payload, config) {
|
|||
lastSeenAt: observedAt,
|
||||
maxAgeMs: config.intervalMs * 3,
|
||||
},
|
||||
debugProbe: normalizeDebugProbe(payload.debugProbe),
|
||||
tabs,
|
||||
};
|
||||
}
|
||||
|
||||
function normalizeDebugProbe(debugProbe) {
|
||||
if (!debugProbe || debugProbe.schema !== "gamecult.loki.chrome_debug_probe.v0") return null;
|
||||
return {
|
||||
...debugProbe,
|
||||
providerId,
|
||||
serviceId,
|
||||
ingestedAt: new Date().toISOString(),
|
||||
};
|
||||
}
|
||||
|
||||
function normalizeExtensionTab(tab) {
|
||||
return {
|
||||
id: String(tab.id ?? ""),
|
||||
|
|
@ -222,11 +233,14 @@ function buildSnapshot(config, cdpObservation, extensionObservation) {
|
|||
},
|
||||
cdp,
|
||||
extension,
|
||||
debugProbe: extensionObservation?.debugProbe ?? null,
|
||||
tabs,
|
||||
summary: {
|
||||
tabCount: tabs.length,
|
||||
inspectableTabCount: tabs.filter((tab) => tab.webSocketDebuggerUrl).length,
|
||||
activeTabCount: tabs.filter((tab) => tab.active).length,
|
||||
debugProbeAttachedTabCount: extensionObservation?.debugProbe?.summary?.attachedTabCount ?? 0,
|
||||
debugProbeAttemptedTabCount: extensionObservation?.debugProbe?.summary?.attemptedTabCount ?? 0,
|
||||
},
|
||||
redaction: {
|
||||
urlQuery: activeObservation ? "preserved" : "not-observed",
|
||||
|
|
@ -234,6 +248,7 @@ function buildSnapshot(config, cdpObservation, extensionObservation) {
|
|||
pageContent: "not-read",
|
||||
cookies: "not-read",
|
||||
source: activeObservation?.source ?? "none",
|
||||
debugProbe: extensionObservation?.debugProbe ? extensionObservation.debugProbe.redaction : "not-run",
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
@ -333,6 +348,14 @@ function buildProviderAdvertisement(snapshot) {
|
|||
cultMeshAddress: "asgard.localhost.loki.chrome/ingest/chrome-extension",
|
||||
portable: true,
|
||||
},
|
||||
{
|
||||
schema: "gamecult.loki.chrome_debug_probe.v0",
|
||||
owner: providerId,
|
||||
authority: "observed",
|
||||
storage: "cultcache-cc",
|
||||
cultMeshAddress: "asgard.localhost.loki.chrome/state/debug-probe",
|
||||
portable: true,
|
||||
},
|
||||
{
|
||||
schema: "gamecult.eve.surface.v1",
|
||||
owner: providerId,
|
||||
|
|
@ -365,6 +388,17 @@ function buildProviderAdvertisement(snapshot) {
|
|||
updatedAt,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "loki.chrome.debug_probe",
|
||||
kind: "cc-export",
|
||||
path: "state/loki.chrome_debug_probe.cc",
|
||||
schemas: ["gamecult.loki.chrome_debug_probe.v0"],
|
||||
redaction: snapshot.debugProbe?.redaction ?? "not-run",
|
||||
freshness: {
|
||||
state: snapshot.debugProbe ? "fresh" : "missing",
|
||||
updatedAt: snapshot.debugProbe?.ingestedAt ?? null,
|
||||
},
|
||||
},
|
||||
],
|
||||
surfaces: [
|
||||
{
|
||||
|
|
@ -391,7 +425,7 @@ function buildProviderAdvertisement(snapshot) {
|
|||
kind: "chrome-extension-ingest",
|
||||
address: snapshot.extension.ingestEndpoint,
|
||||
carries: ["chrome-extension-tab-metadata"],
|
||||
note: "Preferred drop-in Chrome path; extension observes tab metadata and Loki owns persistence.",
|
||||
note: "Preferred drop-in Chrome path; extension observes tab metadata and can run explicit chrome.debugger sweeps.",
|
||||
},
|
||||
{
|
||||
kind: "local-cultcache-witness",
|
||||
|
|
@ -400,6 +434,7 @@ function buildProviderAdvertisement(snapshot) {
|
|||
"state/loki.chrome_snapshot.cc",
|
||||
"state/loki.provider_advertisement.cc",
|
||||
"state/loki.eve_surface.cc",
|
||||
"state/loki.chrome_debug_probe.cc",
|
||||
],
|
||||
},
|
||||
],
|
||||
|
|
@ -453,6 +488,14 @@ function buildEveSurface(snapshot) {
|
|||
metric("source", "Source", snapshot.activeSource, statusTone),
|
||||
metric("tabs", "Tabs", String(snapshot.summary.tabCount), "neutral"),
|
||||
metric("inspectable", "Inspectable", String(snapshot.summary.inspectableTabCount), "neutral"),
|
||||
metric(
|
||||
"debug-probe",
|
||||
"Debug sweep",
|
||||
snapshot.debugProbe
|
||||
? `${snapshot.summary.debugProbeAttachedTabCount}/${snapshot.summary.debugProbeAttemptedTabCount} tabs`
|
||||
: "not run",
|
||||
snapshot.debugProbe ? "ok" : "muted",
|
||||
),
|
||||
{
|
||||
id: "loki.chrome.extension-endpoint",
|
||||
kind: "inspector.kv",
|
||||
|
|
@ -568,6 +611,9 @@ async function publishSnapshot(config, snapshot) {
|
|||
writeTypedDocument(join(config.stateDir, "loki.chrome_snapshot.cc"), snapshot),
|
||||
writeTypedDocument(join(config.stateDir, "loki.provider_advertisement.cc"), advertisement),
|
||||
writeTypedDocument(join(config.stateDir, "loki.eve_surface.cc"), surface),
|
||||
snapshot.debugProbe
|
||||
? writeTypedDocument(join(config.stateDir, "loki.chrome_debug_probe.cc"), snapshot.debugProbe)
|
||||
: Promise.resolve(),
|
||||
]);
|
||||
|
||||
return { advertisement, surface };
|
||||
|
|
@ -627,6 +673,7 @@ function startExtensionIngestServer(config) {
|
|||
schema: snapshot.schema,
|
||||
activeSource: snapshot.activeSource,
|
||||
tabs: snapshot.summary.tabCount,
|
||||
debugProbe: snapshot.debugProbe ? snapshot.debugProbe : null,
|
||||
observedAt: snapshot.observedAt,
|
||||
});
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -38,6 +38,42 @@ try {
|
|||
browser: "Chrome fixture",
|
||||
reportedAt: new Date().toISOString(),
|
||||
reason: "smoke",
|
||||
debugProbe: {
|
||||
schema: "gamecult.loki.chrome_debug_probe.v0",
|
||||
startedAt: new Date().toISOString(),
|
||||
completedAt: new Date().toISOString(),
|
||||
protocolVersion: "1.3",
|
||||
targetCount: 1,
|
||||
targets: [{ id: "fixture-target", tabId: 100, type: "page", title: "Portal das Financas", url: "https://sitfiscal.portaldasfinancas.gov.pt/geral/siteMap", attached: false }],
|
||||
tabs: [{
|
||||
tabId: 100,
|
||||
windowId: 1,
|
||||
title: "Portal das Financas",
|
||||
url: "https://sitfiscal.portaldasfinancas.gov.pt/geral/siteMap",
|
||||
attached: true,
|
||||
attachError: null,
|
||||
commands: [{ method: "Page.getFrameTree", ok: true }],
|
||||
frameTree: { frameTree: { frame: { id: "frame-1", url: "https://sitfiscal.portaldasfinancas.gov.pt/geral/siteMap" } } },
|
||||
runtime: { result: { type: "object", value: { href: "https://sitfiscal.portaldasfinancas.gov.pt/geral/siteMap" } } },
|
||||
performanceMetrics: { metrics: [] },
|
||||
securityState: null,
|
||||
domRoot: { root: { nodeId: 1, nodeName: "#document" } },
|
||||
}],
|
||||
summary: {
|
||||
attemptedTabCount: 1,
|
||||
attachedTabCount: 1,
|
||||
failedTabCount: 0,
|
||||
commandCount: 1,
|
||||
commandFailureCount: 0,
|
||||
},
|
||||
redaction: {
|
||||
pageContent: "not-read",
|
||||
cookies: "not-read",
|
||||
responseBodies: "not-read",
|
||||
domTree: "root-node-only",
|
||||
runtimeEvaluation: "fixture",
|
||||
},
|
||||
},
|
||||
tabs: [
|
||||
{
|
||||
id: 100,
|
||||
|
|
@ -62,12 +98,16 @@ try {
|
|||
}
|
||||
|
||||
const snapshotText = await readFile(join(stateDir, "loki.chrome_snapshot.cc"), "utf8");
|
||||
const debugProbeText = await readFile(join(stateDir, "loki.chrome_debug_probe.cc"), "utf8");
|
||||
if (!snapshotText.includes('"activeSource": "chrome-extension"')) {
|
||||
throw new Error("snapshot did not choose chrome-extension as active source");
|
||||
}
|
||||
if (!snapshotText.includes("Portal das Financas")) {
|
||||
throw new Error("snapshot did not include extension tab");
|
||||
}
|
||||
if (!debugProbeText.includes('"schema": "gamecult.loki.chrome_debug_probe.v0"')) {
|
||||
throw new Error("debug probe witness was not written");
|
||||
}
|
||||
|
||||
console.log("extension ingest smoke passed");
|
||||
} finally {
|
||||
|
|
|
|||
Reference in a new issue