Harden forge NBC worker runtime and MVPD selection
This commit is contained in:
parent
3402f7dab2
commit
8065860449
4 changed files with 102 additions and 3 deletions
|
|
@ -816,6 +816,21 @@ fn advance_nbc_auth_flow(tab: &Arc<Tab>) -> Result<Option<NbcAuthAdvanceResult>>
|
||||||
actions.push(action);
|
actions.push(action);
|
||||||
return true;
|
return true;
|
||||||
}};
|
}};
|
||||||
|
const pressEnter = (node) => {{
|
||||||
|
if (!node) return false;
|
||||||
|
for (const type of ["keydown", "keypress", "keyup"]) {{
|
||||||
|
node.dispatchEvent?.(new KeyboardEvent(type, {{
|
||||||
|
key: "Enter",
|
||||||
|
code: "Enter",
|
||||||
|
which: 13,
|
||||||
|
keyCode: 13,
|
||||||
|
bubbles: true,
|
||||||
|
cancelable: true,
|
||||||
|
}}));
|
||||||
|
}}
|
||||||
|
actions.push("press:enter");
|
||||||
|
return true;
|
||||||
|
}};
|
||||||
const setValue = (node, value) => {{
|
const setValue = (node, value) => {{
|
||||||
if (!node) return false;
|
if (!node) return false;
|
||||||
const prototype = Object.getPrototypeOf(node);
|
const prototype = Object.getPrototypeOf(node);
|
||||||
|
|
@ -835,7 +850,7 @@ fn advance_nbc_auth_flow(tab: &Arc<Tab>) -> Result<Option<NbcAuthAdvanceResult>>
|
||||||
const url = window.location.href || "";
|
const url = window.location.href || "";
|
||||||
const candidates = Array.from(
|
const candidates = Array.from(
|
||||||
document.querySelectorAll(
|
document.querySelectorAll(
|
||||||
"button,a,[role='button'],label,li,div,span,[data-provider-name],[data-provider-id],[data-provider]"
|
"button,a,[role='button'],[role='option'],label,li,[data-provider-name],[data-provider-id],[data-provider]"
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -873,9 +888,24 @@ fn advance_nbc_auth_flow(tab: &Arc<Tab>) -> Result<Option<NbcAuthAdvanceResult>>
|
||||||
}});
|
}});
|
||||||
if (input && normalize(input.value || "") !== normalize(provider)) {{
|
if (input && normalize(input.value || "") !== normalize(provider)) {{
|
||||||
setValue(input, provider);
|
setValue(input, provider);
|
||||||
|
pressEnter(input);
|
||||||
}}
|
}}
|
||||||
|
|
||||||
const providerNode = candidates.find((node) => {{
|
const inputRect = input?.getBoundingClientRect?.() || null;
|
||||||
|
const searchNode = inputRect && candidates.find((node) => {{
|
||||||
|
const text = textOf(node);
|
||||||
|
const rect = node.getBoundingClientRect?.();
|
||||||
|
if (!visible(node) || !rect) return false;
|
||||||
|
const sameRow = Math.abs((rect.top + rect.height / 2) - (inputRect.top + inputRect.height / 2)) <= Math.max(48, inputRect.height);
|
||||||
|
const nearRightEdge = rect.left >= inputRect.right - 24 && rect.left <= inputRect.right + 160;
|
||||||
|
const looksLikeSearch = text.includes("search") || text.includes("find");
|
||||||
|
const compactButton = rect.width <= 96 && rect.height <= Math.max(96, inputRect.height * 2);
|
||||||
|
return (looksLikeSearch || compactButton) && sameRow && nearRightEdge;
|
||||||
|
}});
|
||||||
|
clickNode(searchNode, "click:provider-search");
|
||||||
|
|
||||||
|
const providerNode = candidates
|
||||||
|
.filter((node) => {{
|
||||||
const text = textOf(node);
|
const text = textOf(node);
|
||||||
const providerMeta = normalize([
|
const providerMeta = normalize([
|
||||||
node.getAttribute?.("data-provider-name") || "",
|
node.getAttribute?.("data-provider-name") || "",
|
||||||
|
|
@ -885,7 +915,8 @@ fn advance_nbc_auth_flow(tab: &Arc<Tab>) -> Result<Option<NbcAuthAdvanceResult>>
|
||||||
return visible(node) && providerNeedles.some((needle) =>
|
return visible(node) && providerNeedles.some((needle) =>
|
||||||
text.includes(needle) || providerMeta.includes(needle)
|
text.includes(needle) || providerMeta.includes(needle)
|
||||||
);
|
);
|
||||||
}});
|
}})
|
||||||
|
.sort((a, b) => textOf(a).length - textOf(b).length)[0];
|
||||||
clickNode(providerNode, `click:provider:${{textOf(providerNode).slice(0, 120)}}`);
|
clickNode(providerNode, `click:provider:${{textOf(providerNode).slice(0, 120)}}`);
|
||||||
}}
|
}}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
# ECP-0105: Stable home directory for forge NBC browser workers
|
||||||
|
|
||||||
|
## Why
|
||||||
|
|
||||||
|
The forge NBC worker runs Chrome under a dedicated service user and a persistent profile, but the
|
||||||
|
publish unit was not explicitly setting `HOME`.
|
||||||
|
|
||||||
|
That leaves browser helper processes free to derive state paths from ambient defaults instead of the
|
||||||
|
intended service home. On a long-running forge host, that makes the browser worker more vulnerable
|
||||||
|
to stale locks, crash-report paths, and profile contamination from out-of-band debugging sessions.
|
||||||
|
|
||||||
|
## Decision
|
||||||
|
|
||||||
|
1. Set `HOME=/var/lib/every-channel` on NBC `wt-publish` units, not only on the Xvfb helper units.
|
||||||
|
2. Keep the persistent NBC profile and auth artifacts under `/var/lib/every-channel`.
|
||||||
|
3. Treat the forge NBC browser runtime as a single-service home/profile domain so cleanup and
|
||||||
|
troubleshooting stay deterministic.
|
||||||
|
|
||||||
|
## Consequences
|
||||||
|
|
||||||
|
- Forge NBC launches use the same home directory across the display service and publish service.
|
||||||
|
- Chrome helper processes no longer need to infer state roots from ambient defaults.
|
||||||
|
- Manual debugging sessions must either reuse the service home intentionally or use an isolated
|
||||||
|
profile path to avoid poisoning the live worker profile.
|
||||||
|
|
||||||
|
## Rejected Alternatives
|
||||||
|
|
||||||
|
- Keep only `--user-data-dir` and leave `HOME` implicit: rejected because browser helper processes
|
||||||
|
still derive ancillary paths outside the intended service state root.
|
||||||
|
- Give the publish unit a separate home from the display unit: rejected because it makes the forge
|
||||||
|
browser runtime harder to reason about and recover.
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
# ECP-0106: Forge NBC workers need `/tmp` and search-driven MVPD selection
|
||||||
|
|
||||||
|
## Why
|
||||||
|
|
||||||
|
The forge NBC worker reached two distinct failure domains:
|
||||||
|
|
||||||
|
1. Chrome failed during early startup under the hardened `wt-publish` unit even though the same
|
||||||
|
browser launch worked outside the systemd sandbox.
|
||||||
|
2. Once the browser launch succeeded, the MVPD picker automation could reach the provider gate but
|
||||||
|
still mis-clicked broad page containers instead of the intended provider search result.
|
||||||
|
|
||||||
|
## Decision
|
||||||
|
|
||||||
|
1. Allow NBC `wt-publish` units to write to `/tmp` in addition to the persistent profile and auth
|
||||||
|
directories.
|
||||||
|
2. Treat the NBC MVPD picker as a search-first flow:
|
||||||
|
- type the configured provider name
|
||||||
|
- submit the search explicitly
|
||||||
|
- prefer short, actionable provider-result nodes over generic container matches
|
||||||
|
3. Keep the provider name configurable through `EVERY_CHANNEL_NBC_MVPD_PROVIDER`, with `Verizon Fios`
|
||||||
|
remaining the default.
|
||||||
|
|
||||||
|
## Consequences
|
||||||
|
|
||||||
|
- Forge NBC workers align better with Chrome's actual startup needs under systemd hardening.
|
||||||
|
- MVPD automation becomes less likely to click the whole picker page or other non-provider chrome.
|
||||||
|
- Future provider integrations should extend the same search-first DOM strategy instead of adding
|
||||||
|
brittle page-wide text matches.
|
||||||
|
|
||||||
|
## Rejected Alternatives
|
||||||
|
|
||||||
|
- Disable most systemd hardening for NBC units entirely: rejected because `/tmp` write access is the
|
||||||
|
smallest validated change that unblocks Chrome startup.
|
||||||
|
- Keep broad `div` and `span` provider scans: rejected because they can match large container nodes
|
||||||
|
whose text merely happens to include the provider name.
|
||||||
|
|
@ -646,6 +646,7 @@ in
|
||||||
SystemCallArchitectures = "native";
|
SystemCallArchitectures = "native";
|
||||||
ReadWritePaths =
|
ReadWritePaths =
|
||||||
lib.optionals cfg.control.enable [ "/run/every-channel" ]
|
lib.optionals cfg.control.enable [ "/run/every-channel" ]
|
||||||
|
++ lib.optionals isNbc [ "/tmp" ]
|
||||||
++ lib.optionals isNbc [ cfg.nbc.profileDir cfg.nbc.authScreenshotDir ];
|
++ lib.optionals isNbc [ cfg.nbc.profileDir cfg.nbc.authScreenshotDir ];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -656,6 +657,7 @@ in
|
||||||
EVERY_CHANNEL_NBC_CHROME_PATH = cfg.nbc.chromeBinary;
|
EVERY_CHANNEL_NBC_CHROME_PATH = cfg.nbc.chromeBinary;
|
||||||
EVERY_CHANNEL_NBC_PROFILE_DIR = cfg.nbc.profileDir;
|
EVERY_CHANNEL_NBC_PROFILE_DIR = cfg.nbc.profileDir;
|
||||||
EVERY_CHANNEL_NBC_NO_SANDBOX = if cfg.nbc.noSandbox then "1" else "0";
|
EVERY_CHANNEL_NBC_NO_SANDBOX = if cfg.nbc.noSandbox then "1" else "0";
|
||||||
|
HOME = "/var/lib/every-channel";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue