Implement managed browser security, individual agents and observability
This commit is contained in:
1 parent
ff482ebe23
commit
ea448de2d4
172 files changed
+5404
-347
No files matched your search
@@ -0,0 +1,39 @@
|
||||
// Encrypted developer telemetry only; this is not durable security evidence.
|
||||
import {lstatSync,realpathSync,readdirSync,existsSync,renameSync,unlinkSync,openSync,writeSync,fsyncSync,closeSync} from 'node:fs';
|
||||
import {resolve,join,dirname} from 'node:path';
|
||||
import {randomUUID} from 'node:crypto';
|
||||
|
||||
export function collectorStorage(directory,{segmentBytes=50*1024*1024,retiredSegments=2}={}) {
|
||||
if(!Number.isSafeInteger(segmentBytes)||segmentBytes<1024||!Number.isSafeInteger(retiredSegments)||retiredSegments<0||retiredSegments>10)throw Error('Invalid collector storage limits');
|
||||
directory=resolve(directory);
|
||||
const active=join(directory,'signals.encrypted.ndjson'),legacy=join(directory,'signals.legacy.encrypted.ndjson');
|
||||
let rotations=0,droppedSegments=0,droppedBytes=0,rejectedWrites=0;
|
||||
function guard(){
|
||||
for(let current=directory;;current=dirname(current)){const s=lstatSync(current);if(!s.isDirectory()||s.isSymbolicLink()||realpathSync(current).toLowerCase()!==resolve(current).toLowerCase())throw Error('Unsafe collector directory');if(dirname(current)===current)break;}
|
||||
const names=readdirSync(directory);
|
||||
for(const name of names){if(!['collector-key.dpapi','signals.encrypted.ndjson','signals.legacy.encrypted.ndjson'].includes(name)&&!/^signals\.rolling\.[0-9]{13}\.[a-f0-9-]{36}\.encrypted\.ndjson$/.test(name))throw Error('Unexpected collector storage entry');const path=join(directory,name),s=lstatSync(path);if(!s.isFile()||s.isSymbolicLink()||s.nlink!==1||realpathSync(path).toLowerCase()!==path.toLowerCase())throw Error('Unsafe collector storage entry');}
|
||||
return names.filter(n=>n.startsWith('signals.rolling.')).sort();
|
||||
}
|
||||
guard();
|
||||
// The first old archive is pinned and never retention-deleted, including after restart.
|
||||
function prune(){let retired=guard();while(retired.length>retiredSegments){const name=retired.shift(),path=join(directory,name);guard();const size=lstatSync(path).size;unlinkSync(path);droppedSegments++;droppedBytes+=size;}}
|
||||
function rotate(){
|
||||
guard();
|
||||
if(existsSync(active)){
|
||||
if(!existsSync(legacy))renameSync(active,legacy);
|
||||
else renameSync(active,join(directory,`signals.rolling.${Date.now()}.${randomUUID()}.encrypted.ndjson`));
|
||||
rotations++;
|
||||
}
|
||||
prune();
|
||||
}
|
||||
function append(line){
|
||||
try{
|
||||
guard();const bytes=Buffer.byteLength(line);if(bytes>segmentBytes)throw Error('Collector batch exceeds segment budget');prune();
|
||||
const size=existsSync(active)?lstatSync(active).size:0;if(size+bytes>segmentBytes)rotate();
|
||||
guard();const fd=openSync(active,existsSync(active)?'a':'wx',0o600);
|
||||
try{const buffer=Buffer.from(line);let offset=0;while(offset<buffer.length){const count=writeSync(fd,buffer,offset,buffer.length-offset);if(count<1)throw Error('Collector short write');offset+=count;}fsyncSync(fd);}finally{closeSync(fd);}
|
||||
}catch(error){rejectedWrites++;throw error;}
|
||||
}
|
||||
function summary(){const retired=guard(),activeBytes=existsSync(active)?lstatSync(active).size:0,legacyBytes=existsSync(legacy)?lstatSync(legacy).size:0;return {storedBytes:activeBytes,activeBytes,legacyBytes,retiredBytes:retired.reduce((n,name)=>n+lstatSync(join(directory,name)).size,0),retiredSegments:retired.length,segmentBytes,maxRollingBytes:segmentBytes*(retiredSegments+1),rotations,droppedSegments,droppedBytes,rejectedWrites};}
|
||||
return {append,summary};
|
||||
}
|
||||
Reference in new issue
Block a user