mirror of
https://github.com/BigBodyCobain/Shadowbroker.git
synced 2026-08-11 05:00:30 +02:00
v0.9.6: InfoNet hashchain, Wormhole gate encryption, mesh reputation, 16 community contributors
Gate messages now propagate via the Infonet hashchain as encrypted blobs — every node syncs them through normal chain sync while only Gate members with MLS keys can decrypt. Added mesh reputation system, peer push workers, voluntary Wormhole opt-in for node participation, fork recovery, killwormhole scripts, obfuscated terminology, and hardened the self-updater to protect encryption keys and chain state during updates. New features: Shodan search, train tracking, Sentinel Hub imagery, 8 new intelligence layers, CCTV expansion to 11,000+ cameras across 6 countries, Mesh Terminal CLI, prediction markets, desktop-shell scaffold, and comprehensive mesh test suite (215 frontend + backend tests passing). Community contributors: @wa1id, @AlborzNazari, @adust09, @Xpirix, @imqdcr, @csysp, @suranyami, @chr0n1x, @johan-martensson, @singularfailure, @smithbh, @OrfeoTerkuci, @deuza, @tm-const, @Elhard1, @ttulttul
This commit is contained in:
@@ -1,5 +1,10 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { classifyAircraft, HELI_TYPES, TURBOPROP_TYPES, BIZJET_TYPES } from '@/utils/aircraftClassification';
|
||||
import {
|
||||
classifyAircraft,
|
||||
HELI_TYPES,
|
||||
TURBOPROP_TYPES,
|
||||
BIZJET_TYPES,
|
||||
} from '@/utils/aircraftClassification';
|
||||
|
||||
describe('classifyAircraft', () => {
|
||||
// ─── Helicopter classification ────────────────────────────────────────────
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
const idbStore = new Map<string, unknown>();
|
||||
|
||||
vi.mock('@/mesh/meshKeyStore', () => ({
|
||||
getKey: vi.fn(async (id: string) => idbStore.get(id) ?? null),
|
||||
setKey: vi.fn(async (id: string, key: unknown) => {
|
||||
idbStore.set(id, key);
|
||||
}),
|
||||
deleteKey: vi.fn(async (id: string) => {
|
||||
idbStore.delete(id);
|
||||
}),
|
||||
}));
|
||||
|
||||
function makeStorage() {
|
||||
const values = new Map<string, string>();
|
||||
return {
|
||||
getItem: (key: string) => values.get(key) ?? null,
|
||||
setItem: (key: string, value: string) => void values.set(key, value),
|
||||
removeItem: (key: string) => void values.delete(key),
|
||||
clear: () => void values.clear(),
|
||||
};
|
||||
}
|
||||
|
||||
function bufToBase64(buf: ArrayBuffer): string {
|
||||
return btoa(String.fromCharCode(...new Uint8Array(buf)));
|
||||
}
|
||||
|
||||
async function provisionLocalIdentity(): Promise<void> {
|
||||
const meshIdentity = await import('@/mesh/meshIdentity');
|
||||
localStorage.setItem('sb_mesh_pubkey', 'test-pub');
|
||||
localStorage.setItem('sb_mesh_node_id', '!sb_sensitive123456');
|
||||
localStorage.setItem('sb_mesh_sovereignty_accepted', 'true');
|
||||
const keyPair = (await crypto.subtle.generateKey(
|
||||
{ name: 'ECDH', namedCurve: 'P-256' },
|
||||
false,
|
||||
['deriveKey', 'deriveBits'],
|
||||
)) as CryptoKeyPair;
|
||||
const publicRaw = await crypto.subtle.exportKey('raw', keyPair.publicKey);
|
||||
localStorage.setItem('sb_mesh_dh_pubkey', bufToBase64(publicRaw));
|
||||
localStorage.setItem('sb_mesh_dh_algo', 'ECDH');
|
||||
idbStore.set('sb_mesh_dh_priv', keyPair.privateKey);
|
||||
meshIdentity.getNodeIdentity();
|
||||
}
|
||||
|
||||
describe('identityBoundSensitiveStorage', () => {
|
||||
beforeEach(() => {
|
||||
vi.resetModules();
|
||||
idbStore.clear();
|
||||
Object.defineProperty(globalThis, 'localStorage', {
|
||||
value: makeStorage(),
|
||||
configurable: true,
|
||||
writable: true,
|
||||
});
|
||||
Object.defineProperty(globalThis, 'sessionStorage', {
|
||||
value: makeStorage(),
|
||||
configurable: true,
|
||||
writable: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('stores encrypted values in sensitive storage and keeps them out of localStorage', async () => {
|
||||
await provisionLocalIdentity();
|
||||
const storage = await import('@/lib/identityBoundSensitiveStorage');
|
||||
|
||||
await storage.persistIdentityBoundSensitiveValue(
|
||||
'sb_access_requests:test',
|
||||
'SB-ACCESS-REQUESTS-STORAGE-V1',
|
||||
[{ sender_id: 'alice', ts: 1 }],
|
||||
);
|
||||
|
||||
expect(String(sessionStorage.getItem('sb_access_requests:test') ?? '')).toMatch(/^enc:/);
|
||||
expect(localStorage.getItem('sb_access_requests:test')).toBeNull();
|
||||
|
||||
const hydrated = await storage.loadIdentityBoundSensitiveValue(
|
||||
'sb_access_requests:test',
|
||||
'SB-ACCESS-REQUESTS-STORAGE-V1',
|
||||
[],
|
||||
);
|
||||
expect(hydrated).toEqual([{ sender_id: 'alice', ts: 1 }]);
|
||||
});
|
||||
|
||||
it('migrates legacy plaintext sensitive values into encrypted session-backed storage', async () => {
|
||||
await provisionLocalIdentity();
|
||||
const storage = await import('@/lib/identityBoundSensitiveStorage');
|
||||
|
||||
localStorage.setItem('sb_mesh_muted', JSON.stringify(['alice', 'bob']));
|
||||
|
||||
const hydrated = await storage.loadIdentityBoundSensitiveValue(
|
||||
'sb_mesh_muted:!sb_sensitive123456',
|
||||
'SB-MUTED-LIST-V1',
|
||||
[],
|
||||
{ legacyKey: 'sb_mesh_muted' },
|
||||
);
|
||||
|
||||
expect(hydrated).toEqual(['alice', 'bob']);
|
||||
expect(String(sessionStorage.getItem('sb_mesh_muted:!sb_sensitive123456') ?? '')).toMatch(/^enc:/);
|
||||
expect(localStorage.getItem('sb_mesh_muted')).toBeNull();
|
||||
expect(sessionStorage.getItem('sb_mesh_muted')).toBeNull();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,87 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
function makeStorage() {
|
||||
const values = new Map<string, string>();
|
||||
return {
|
||||
getItem: (key: string) => values.get(key) ?? null,
|
||||
setItem: (key: string, value: string) => void values.set(key, value),
|
||||
removeItem: (key: string) => void values.delete(key),
|
||||
clear: () => void values.clear(),
|
||||
};
|
||||
}
|
||||
|
||||
describe('privacyBrowserStorage', () => {
|
||||
beforeEach(() => {
|
||||
vi.resetModules();
|
||||
Object.defineProperty(globalThis, 'localStorage', {
|
||||
value: makeStorage(),
|
||||
configurable: true,
|
||||
writable: true,
|
||||
});
|
||||
Object.defineProperty(globalThis, 'sessionStorage', {
|
||||
value: makeStorage(),
|
||||
configurable: true,
|
||||
writable: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('stores sensitive items in sessionStorage by default', async () => {
|
||||
const mod = await import('@/lib/privacyBrowserStorage');
|
||||
|
||||
mod.setSensitiveBrowserItem('secret-key', 'alpha');
|
||||
|
||||
expect(mod.getSensitiveBrowserStorageMode()).toBe('session');
|
||||
expect(sessionStorage.getItem('secret-key')).toBe('alpha');
|
||||
expect(localStorage.getItem('secret-key')).toBeNull();
|
||||
expect(mod.getSensitiveBrowserItem('secret-key')).toBe('alpha');
|
||||
});
|
||||
|
||||
it('stores privacy preferences in session storage when session mode is enabled', async () => {
|
||||
const mod = await import('@/lib/privacyBrowserStorage');
|
||||
|
||||
mod.setSessionModePreference(true);
|
||||
mod.setPrivacyStrictPreference(true, { sessionMode: true });
|
||||
mod.setPrivacyProfilePreference('high', { sessionMode: true });
|
||||
|
||||
expect(mod.getSessionModePreference()).toBe(true);
|
||||
expect(mod.getPrivacyStrictPreference()).toBe(true);
|
||||
expect(mod.getPrivacyProfilePreference()).toBe('high');
|
||||
expect(sessionStorage.getItem('sb_mesh_session_mode')).toBe('true');
|
||||
expect(sessionStorage.getItem('sb_privacy_strict')).toBe('true');
|
||||
expect(sessionStorage.getItem('sb_privacy_profile')).toBe('high');
|
||||
expect(localStorage.getItem('sb_mesh_session_mode')).toBeNull();
|
||||
expect(localStorage.getItem('sb_privacy_strict')).toBeNull();
|
||||
expect(localStorage.getItem('sb_privacy_profile')).toBeNull();
|
||||
});
|
||||
|
||||
it('persists session mode locally only when the user explicitly disables it', async () => {
|
||||
const mod = await import('@/lib/privacyBrowserStorage');
|
||||
|
||||
mod.setSessionModePreference(false);
|
||||
|
||||
expect(mod.getSessionModePreference()).toBe(false);
|
||||
expect(localStorage.getItem('sb_mesh_session_mode')).toBe('false');
|
||||
expect(sessionStorage.getItem('sb_mesh_session_mode')).toBeNull();
|
||||
});
|
||||
|
||||
it('stores sensitive items in sessionStorage when privacy strict is enabled', async () => {
|
||||
localStorage.setItem('sb_privacy_strict', 'true');
|
||||
const mod = await import('@/lib/privacyBrowserStorage');
|
||||
|
||||
mod.setSensitiveBrowserItem('secret-key', 'bravo');
|
||||
|
||||
expect(mod.getSensitiveBrowserStorageMode()).toBe('session');
|
||||
expect(sessionStorage.getItem('secret-key')).toBe('bravo');
|
||||
expect(localStorage.getItem('secret-key')).toBeNull();
|
||||
});
|
||||
|
||||
it('migrates legacy localStorage values into sessionStorage in strict mode', async () => {
|
||||
localStorage.setItem('sb_privacy_strict', 'true');
|
||||
localStorage.setItem('secret-key', 'charlie');
|
||||
const mod = await import('@/lib/privacyBrowserStorage');
|
||||
|
||||
expect(mod.getSensitiveBrowserItem('secret-key')).toBe('charlie');
|
||||
expect(sessionStorage.getItem('secret-key')).toBe('charlie');
|
||||
expect(localStorage.getItem('secret-key')).toBeNull();
|
||||
});
|
||||
});
|
||||
@@ -70,7 +70,8 @@ describe('computeNightPolygon', () => {
|
||||
.filter(([lng]: number[]) => lng >= -180 && lng <= 180)
|
||||
.slice(0, 361)
|
||||
.map(([, lat]: number[]) => lat);
|
||||
const avgLat = terminatorLats.reduce((a: number, b: number) => a + b, 0) / terminatorLats.length;
|
||||
const avgLat =
|
||||
terminatorLats.reduce((a: number, b: number) => a + b, 0) / terminatorLats.length;
|
||||
expect(avgLat).toBeLessThan(15);
|
||||
});
|
||||
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import {
|
||||
buildBoundsQuery,
|
||||
coarsenViewBounds,
|
||||
expandBoundsToRadius,
|
||||
} from '@/lib/viewportPrivacy';
|
||||
|
||||
describe('viewport privacy helper', () => {
|
||||
it('coarsens narrow bounds outward without clipping the original view', () => {
|
||||
const original = {
|
||||
south: 33.612,
|
||||
west: -84.452,
|
||||
north: 33.781,
|
||||
east: -84.211,
|
||||
};
|
||||
|
||||
const coarse = coarsenViewBounds(original);
|
||||
|
||||
expect(coarse.south).toBeLessThanOrEqual(original.south);
|
||||
expect(coarse.west).toBeLessThanOrEqual(original.west);
|
||||
expect(coarse.north).toBeGreaterThanOrEqual(original.north);
|
||||
expect(coarse.east).toBeGreaterThanOrEqual(original.east);
|
||||
expect(coarse.south).toBe(33.6);
|
||||
expect(coarse.west).toBe(-84.5);
|
||||
expect(coarse.north).toBe(33.8);
|
||||
expect(coarse.east).toBe(-84.2);
|
||||
});
|
||||
|
||||
it('canonicalizes the bounds query so nearby pans in the same coarse cell dedupe', () => {
|
||||
const a = buildBoundsQuery({
|
||||
south: 47.6011,
|
||||
west: -122.3484,
|
||||
north: 47.6902,
|
||||
east: -122.2012,
|
||||
});
|
||||
const b = buildBoundsQuery({
|
||||
south: 47.6039,
|
||||
west: -122.3441,
|
||||
north: 47.6883,
|
||||
east: -122.2051,
|
||||
});
|
||||
|
||||
expect(a).toBe('?s=47.60&w=-122.35&n=47.70&e=-122.20');
|
||||
expect(b).toBe(a);
|
||||
});
|
||||
|
||||
it('expands bounds to a fixed preload radius around the current view center', () => {
|
||||
const original = {
|
||||
south: 39.55,
|
||||
west: -105.25,
|
||||
north: 39.95,
|
||||
east: -104.75,
|
||||
};
|
||||
|
||||
const expanded = expandBoundsToRadius(original, 3000);
|
||||
|
||||
expect(expanded.south).toBeLessThanOrEqual(original.south);
|
||||
expect(expanded.west).toBeLessThanOrEqual(original.west);
|
||||
expect(expanded.north).toBeGreaterThanOrEqual(original.north);
|
||||
expect(expanded.east).toBeGreaterThanOrEqual(original.east);
|
||||
expect(expanded.north - expanded.south).toBeGreaterThan(80);
|
||||
expect(expanded.east - expanded.west).toBeGreaterThan(90);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user