Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
35d6794
fix(android): serve each React surface's secure rects by its own origin
krassx Oct 4, 2026
76eeb56
fix(ios): keep per-surface secure lanes with their own origin
krassx Oct 4, 2026
3d58f09
fix(privacy): publish <BugseeSecure> and managed nodes on their own s…
krassx Oct 4, 2026
ab84802
test(e2e): cover <BugseeSecure> inside a <Modal> on both surfaces
krassx Oct 4, 2026
99ae355
fix(privacy): place each Modal by its own root, keyed off the fiber tree
krassx Oct 5, 2026
7895840
test(e2e): find each secure view by its colour, and add the discrimin…
krassx Oct 5, 2026
020b682
test(e2e): an opaque pageSheet hides the main secure view
krassx Oct 5, 2026
5877a03
test(secure): expect the error's name in BugseeSecure's warning
krassx Oct 5, 2026
9ec0e5e
fix(privacy): bound and clamp what the secure store serves, and fail …
krassx Oct 5, 2026
b349408
test(e2e): check the Modal's secure view in every video frame too
krassx Oct 5, 2026
53023ab
fix(privacy): drop a JS runtime's Modal surfaces when it goes away
krassx Oct 5, 2026
be4bf5e
chore(android): log at debug what a JS runtime's claim and release drop
krassx Oct 5, 2026
756d531
fix(privacy,android): ignore secure-store writes from a runtime whose…
krassx Oct 5, 2026
3cbce1b
fix(privacy,ios): ignore secure-store writes from a runtime whose cla…
krassx Oct 5, 2026
f0a421b
fix(ios): find a Modal host by class and React tag, not viewWithTag:
krassx Oct 5, 2026
e3463b7
fix(android): a runtime claim empties the main surface's rectangles
krassx Oct 5, 2026
86a0b75
fix(ios): a runtime claim empties the main surface's rectangles
krassx Oct 5, 2026
311de7c
fix(privacy,ios): place a Modal lane by the current runtime's own host
krassx Oct 5, 2026
a72596f
fix(privacy,ios): a named Modal host that is not found yet fails closed
krassx Oct 5, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion examples/bare/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
* is scenarios/secure.tsx, `attributes`/`attributes-persist` are
* scenarios/attributes.ts, `e2e-native-smoke` / `native-crash-*` are
* scenarios/native.ts, `blackout`, `blackout-prelaunch`,
* `secure-component` and `view-tree` are scenarios/privacy.tsx, and
* `secure-component`, `secure-modal`, `secure-modal-translucent`,
* `secure-modal-sheet` and `view-tree` are scenarios/privacy.tsx, and
* `exc-*` are scenarios/exceptions.tsx, `rp-*` are
* scenarios/reporting.ts, `log-filter` is scenarios/log-filter.ts,
* `breadcrumb-filter` is scenarios/breadcrumb-filter.ts,
Expand Down
71 changes: 71 additions & 0 deletions examples/bare/e2e/media.ts
Original file line number Diff line number Diff line change
Expand Up @@ -415,3 +415,74 @@ export function heldBlackout(
`${BLACKOUT_MIN_DARK_S}s until a bright frame`,
};
}

/** An RGB colour, each channel 0-255. */
export interface Colour {
readonly r: number;
readonly g: number;
readonly b: number;
}

/** How far each channel may stray from a test colour: lossy screenshots shift it. */
export const COLOUR_TOLERANCE = 48;

/** `#RRGGBB` as a `Colour`. */
export function colourOf(hex: string): Colour {
const match = /^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i.exec(hex);
if (match === null) {
throw new Error(`colourOf: not #RRGGBB: ${hex}`);
}
return { r: parseInt(match[1]!, 16), g: parseInt(match[2]!, 16), b: parseInt(match[3]!, 16) };
}

/**
* Every pixel of `file`'s first frame within `tolerance` of `colour` on all
* three channels: how many, and their bounding box in image pixels (`right`
* and `bottom` exclusive), or `null` for none. Decodes the whole frame, so it
* finds a solid test colour wherever it really is on screen.
*/
export async function colourPixels(
file: string,
colour: Colour,
tolerance = COLOUR_TOLERANCE,
): Promise<{
count: number;
box: { left: number; top: number; right: number; bottom: number } | null;
width: number;
height: number;
}> {
const { width, height } = await imageSize(file);
const { stdout } = await runBinary('ffmpeg', [
'-v', 'error',
'-i', file,
'-frames:v', '1',
'-pix_fmt', 'rgb24',
'-f', 'rawvideo',
'-',
]);
if (stdout.length !== width * height * 3) {
throw new Error(`colourPixels: ${file}: expected ${width * height * 3} raw byte(s), got ${stdout.length}`);
}
let count = 0;
let left = width;
let top = height;
let right = 0;
let bottom = 0;
for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
const at = (y * width + x) * 3;
if (
Math.abs(stdout[at]! - colour.r) <= tolerance &&
Math.abs(stdout[at + 1]! - colour.g) <= tolerance &&
Math.abs(stdout[at + 2]! - colour.b) <= tolerance
) {
count++;
left = Math.min(left, x);
top = Math.min(top, y);
right = Math.max(right, x + 1);
bottom = Math.max(bottom, y + 1);
}
}
}
return { count, box: count === 0 ? null : { left, top, right, bottom }, width, height };
}
Loading