Files
MikrotikManager/backend/scripts/database-writer-profile-operator.test.mts
T

55 lines
6.2 KiB
TypeScript

import test from 'node:test'
import assert from 'node:assert/strict'
import {generateKeyPairSync,createHash,X509Certificate,sign} from 'node:crypto'
import {rootCertificates} from 'node:tls'
import {createRequire} from 'node:module'
import {approval,reconciliationApproval,poolOptions,STARTUP_OPTIONS,assertJsDriverEnvironment} from './writer-profile/operator-contract.mjs'
import {bytes,canonical,POLICY_DOMAIN,signedEnvelope} from '../src/db/application-writer-profile-format.ts'
const hash=(v:Buffer|string)=>createHash('sha256').update(v).digest('hex')
const pinned=()=>({format:'application-writer-publication-approval-v1',manifestSha256:'a'.repeat(64),definitionSha256:'b'.repeat(64),catalogSha256:'c'.repeat(64),expectedHeadSha256:null,validityMs:86400000,expiresAt:Date.now()+60000})
test('approval is explicit, exact, future-bounded and does not learn missing expected fields',()=>{
const value=pinned();assert.deepEqual(approval(value),value)
for(const change of [(v:any)=>delete v.definitionSha256,(v:any)=>delete v.catalogSha256,(v:any)=>v.expiresAt=Date.now(),(v:any)=>v.validityMs=86400001,(v:any)=>v.expectedHeadSha256='observed',(v:any)=>v.provider=true]){const v=pinned();change(v);assert.throws(()=>approval(v))}
})
test('isolated Pool1 options require pinned CA/host/role/database and native TLS verification',()=>{
const ca=Buffer.from(rootCertificates[0]),request={roles:{publisher:{name:'installation_policy_signer'}},source:{databaseName:'owned'},tls:{serverName:'db.example.invalid',caSha256:hash(ca),spkiSha256:hash(new X509Certificate(ca).publicKey.export({format:'der',type:'spki'}))}}
const transport={host:'db.example.invalid',port:5432,user:'installation_policy_signer',password:'test-only',database:'owned'},config=poolOptions(transport,request,ca)
assert.equal(config.max,1);assert.equal(config.connectionTimeoutMillis,5000);assert.equal(config.ssl.rejectUnauthorized,true)
assert(config.ssl.checkServerIdentity('attacker.invalid',new X509Certificate(ca).toLegacyObject()) instanceof Error)
for(const changed of [{...transport,user:'admin'},{...transport,host:'attacker.invalid'},{...transport,database:'other'},{...transport,connectionString:'postgres://env-substitution'},{...transport,port:0}])assert.throws(()=>poolOptions(changed,request,ca))
assert.throws(()=>poolOptions(transport,request,Buffer.from('not approved CA')))
})
test('real production envelope parser accepts operator-key signature and rejects tamper/foreign key',()=>{
const now=Date.now(),{publicKey,privateKey}=generateKeyPairSync('ed25519'),installation={installationId:'788629ed-e00d-4af8-bc33-c705e5a7b62d',publicKeyPem:publicKey.export({type:'spki',format:'pem'}).toString(),publicKeySpkiSha256:hash(publicKey.export({type:'spki',format:'der'}))}
const body={format:'application-writer-policy-v1',installationId:installation.installationId,epoch:'1',issuedAt:now,notBefore:now,notAfter:now+86400000,source:{systemIdentifier:'7348518042453055651',databaseOid:'16384',databaseName:'owned',serverVersionNum:180004,inRecovery:false},sourceObservationSha256:'a'.repeat(64),roles:{runtime:'20001',operator:'20002',capture:'20003',publisher:'20004',control:'20005'},manifestSha256:'b'.repeat(64),definitionSha256:'c'.repeat(64),catalogSha256:'d'.repeat(64),futureLeaves:'fixed12-native-policy-v1'}
const signature=sign(null,Buffer.from(POLICY_DOMAIN+canonical(body)),privateKey).toString('base64url')
assert.equal(signedEnvelope(bytes({body,signature}),'policy',installation).body.epoch,'1')
assert.throws(()=>signedEnvelope(bytes({body:{...body,catalogSha256:'e'.repeat(64)},signature}),'policy',installation))
const other=generateKeyPairSync('ed25519').publicKey;assert.throws(()=>signedEnvelope(bytes({body,signature}),'policy',{...installation,publicKeyPem:other.export({type:'spki',format:'pem'}).toString(),publicKeySpkiSha256:hash(other.export({type:'spki',format:'der'}))}))
})
test('actual pg ConnectionParameters cannot inherit malicious PGOPTIONS and native selection refuses',()=>{
const require=createRequire(new URL('../package.json',import.meta.url)),ConnectionParameters=require('pg/lib/connection-parameters')
const original=process.env.PGOPTIONS;process.env.PGOPTIONS='-c role=attacker -c search_path=attacker'
try{
const ca=Buffer.from(rootCertificates[0]),request={roles:{publisher:{name:'installation_policy_signer'}},source:{databaseName:'owned'},tls:{serverName:'db.example.invalid',caSha256:hash(ca),spkiSha256:'a'.repeat(64)}}
const config=poolOptions({host:'db.example.invalid',port:5432,user:'installation_policy_signer',password:'test-only',database:'owned'},request,ca)
const actual=new ConnectionParameters(config);assert.equal(actual.options,STARTUP_OPTIONS);assert(!actual.options.includes('attacker'));assert.equal(actual.user,'installation_policy_signer');assert.equal(actual.ssl.rejectUnauthorized,true)
}finally{if(original===undefined)delete process.env.PGOPTIONS;else process.env.PGOPTIONS=original}
assert.doesNotThrow(()=>assertJsDriverEnvironment({}));for(const value of ['1','0','',undefined])assert.throws(()=>assertJsDriverEnvironment({NODE_PG_FORCE_NATIVE:value}))
})
test('reconciliation accepts expired original approval as copied inspection data; publication still refuses',()=>{
const value={...pinned(),expiresAt:1},copy=reconciliationApproval(value)
assert.deepEqual(copy,value);assert.notEqual(copy,value);assert.throws(()=>approval(value))
copy.catalogSha256='d'.repeat(64);assert.equal(value.catalogSha256,'c'.repeat(64))
const future=pinned();assert.deepEqual(approval(future),reconciliationApproval(future))
})
test('reconciliation keeps exact digest/head/schema and original lifetime bounds',()=>{
for(const mutate of [(v:any)=>delete v.definitionSha256,(v:any)=>v.catalogSha256='observed',(v:any)=>v.expectedHeadSha256='foreign',(v:any)=>v.extra=true,(v:any)=>v.format='other',(v:any)=>v.validityMs=0,(v:any)=>v.validityMs=86400001,(v:any)=>v.expiresAt=0,(v:any)=>v.expiresAt=-1,(v:any)=>v.expiresAt=Number.MAX_SAFE_INTEGER+1]){
const value={...pinned(),expiresAt:1};mutate(value);assert.throws(()=>reconciliationApproval(value));assert.throws(()=>approval(value))
}
})
test('publication expiry guard remains mandatory at the real clock boundary',()=>{
const value=pinned();value.expiresAt=Date.now();assert.throws(()=>approval(value));assert.deepEqual(reconciliationApproval(value),value)
})