fix(stealth): address security review findings

1. HIGH — Function.toString Map exfiltration:
   Replaced Map with WeakMap + bound methods. A malicious page could
   monkeypatch Map.prototype.has to capture the override store, then
   use it to cloak malicious functions as [native code]. WeakMap with
   pre-bound has/get methods prevents this side-channel.

2. MEDIUM — Static GPU fingerprint:
   Default GPU renderer now randomly selects from 5 common Apple chip
   variants (M1, M1 Pro, M1 Max, M2, M3) per session. Prevents sites
   from building a static GStack-specific fingerprint signature.

3. Tests updated: 54 total (35 unit + 19 e2e), 0 failures.
   Added tests for WeakMap usage and GPU randomization.
This commit is contained in:
gstack
2026-04-21 03:08:06 +00:00
parent 8df1c003b5
commit 1eae837260
3 changed files with 39 additions and 7 deletions
+3 -3
View File
@@ -65,7 +65,7 @@ describe('stealth e2e — fingerprint verification', () => {
expect(vendor).not.toContain('SwiftShader');
});
test('WebGL renderer is spoofed to Apple M1 Pro', async () => {
test('WebGL renderer is spoofed to an Apple chip', async () => {
const renderer = await page.evaluate(() => {
const canvas = document.createElement('canvas');
const gl = canvas.getContext('webgl');
@@ -75,7 +75,7 @@ describe('stealth e2e — fingerprint verification', () => {
return gl.getParameter(ext.UNMASKED_RENDERER_WEBGL);
});
expect(renderer).toBeTruthy();
expect(renderer).toContain('Apple M1 Pro');
expect(renderer).toMatch(/Apple.*M[123]/);
expect(renderer).not.toContain('SwiftShader');
expect(renderer).not.toContain('llvmpipe');
});
@@ -91,7 +91,7 @@ describe('stealth e2e — fingerprint verification', () => {
});
// WebGL2 might not be available in all environments
if (renderer !== null) {
expect(renderer).toContain('Apple M1 Pro');
expect(renderer).toMatch(/Apple.*M[123]/);
}
});
+17 -1
View File
@@ -110,6 +110,21 @@ describe('init script coverage', () => {
expect(source).toContain('[native code]');
});
test('uses WeakMap (not Map) for toString overrides to prevent exfiltration', () => {
// Security: Map can be exfiltrated via Map.prototype.has monkeypatching.
// WeakMap with bound methods prevents this attack vector.
expect(source).toContain('new WeakMap');
expect(source).toContain('WeakMap.prototype.has.bind');
expect(source).toContain('WeakMap.prototype.get.bind');
// Must NOT use plain Map for the override store
expect(source).not.toMatch(/new Map[<(]/);
});
test('GPU renderer varies across sessions (anti-fingerprint)', () => {
expect(source).toContain('gpuVariants');
expect(source).toContain('Math.random');
});
test('handles mediaDevices for containers', () => {
expect(source).toContain('mediaDevices');
expect(source).toContain('enumerateDevices');
@@ -184,7 +199,8 @@ describe('applyStealthPatches API', () => {
await applyStealthPatches(mockContext);
const [vendor, renderer] = receivedArg as [string, string];
expect(vendor).toContain('Apple');
expect(renderer).toContain('M1 Pro');
// Renderer varies across sessions but should always be an Apple chip
expect(renderer).toMatch(/Apple.*M[123]/);
});
test('init script function is serializable (no closures over Node APIs)', async () => {