Files
KArtSell.Aegis/docs/Design/kbx-foundation-v36/scripts/validate-provider-governance.mjs
T

40 lines
4.7 KiB
JavaScript

import fs from 'node:fs'
const read=p=>JSON.parse(fs.readFileSync(p,'utf8'))
const src=read('contracts/providers/kbx.providers.json')
const manifest=read('generated/provider-manifest.json')
const errors=[]
const ids=new Set()
const officialHosts=['openapi.krx.co.kr','opendart.fss.or.kr','apiportal.koreainvestment.com','github.com']
for(const p of src.providers??[]){
if(ids.has(p.id))errors.push(`duplicate provider ${p.id}`);ids.add(p.id)
if(p.mutationAllowed)errors.push(`${p.id}: v20 providers must remain read-only`)
for(const u of p.officialSources??[]){const host=new URL(u).host;if(!officialHosts.includes(host))errors.push(`${p.id}: non-official source host ${host}`)}
}
const krx=src.providers.find(x=>x.id==='provider.krx.openapi')
if(!krx)errors.push('missing KRX provider')
else {if(krx.official.authentication?.headerName!=='AUTH_KEY')errors.push('KRX AUTH_KEY header drift');if(krx.official.numericRateLimit!==null)errors.push('KRX universal numeric rate limit must stay unknown unless officially verified');if(krx.kbxPolicy.allowedHostSuffix!=='.krx.co.kr')errors.push('KRX approved host suffix guard missing')}
const dart=src.providers.find(x=>x.id==='provider.opendart')
if(!dart)errors.push('missing OPENDART provider')
else {if(dart.official.baseUri!=='https://opendart.fss.or.kr/api/')errors.push('OPENDART base URI drift');if(dart.official.authentication?.parameterName!=='crtfc_key'||dart.official.authentication?.length!==40)errors.push('OPENDART auth contract drift');for(const c of ['000','013','020','800','900','901'])if(!dart.official.statusCodes?.[c])errors.push(`OPENDART status missing ${c}`);for(const op of ['list.json','company.json','corpCode.xml'])if(!(dart.operations??[]).some(x=>x.path===op))errors.push(`OPENDART operation missing ${op}`)}
const kis=src.providers.find(x=>x.id==='provider.kis.market-data')
if(!kis)errors.push('missing KIS provider')
else {if(kis.official.authentication?.tokenPath!=='/oauth2/tokenP'||kis.official.authentication?.grantType!=='client_credentials')errors.push('KIS OAuth contract drift');if(kis.official.productionRequestsPerSecond!==18||kis.official.sandboxRequestsPerSecond!==1||kis.official.tokenRequestsPerSecond!==1)errors.push('KIS official rate-limit facts drift');const price=(kis.operations??[]).find(x=>x.id==='kis.domestic-stock.current-price');if(!price||price.method!=='GET'||price.path!=='/uapi/domestic-stock/v1/quotations/inquire-price'||price.trId!=='FHKST01010100')errors.push('KIS current-price official sample contract drift')}
if(manifest.providers.length!==src.providers.length)errors.push('provider manifest count drift')
const ts=fs.readFileSync('packages/kbx-contracts/src/generated/providerCatalog.ts','utf8'),cs=fs.readFileSync('backend/Shared/Providers/Generated/KbxExternalProviderCatalog.g.cs','utf8')
if(!ts.includes(manifest.sourceSha256)||!cs.includes(manifest.sourceSha256))errors.push('provider source SHA parity missing')
const krxCode=fs.readFileSync('backend/Shared/Providers/KrxOpenApiAdapter.cs','utf8')
for(const token of ['AUTH_KEY','.krx.co.kr','IKbxProviderSecretStore'])if(!krxCode.includes(token))errors.push(`KRX adapter missing ${token}`)
const dartCode=fs.readFileSync('backend/Shared/Providers/OpenDartAdapter.cs','utf8')
for(const token of ['crtfc_key','"013"','"020"','"800"','"900"'])if(!dartCode.includes(token))errors.push(`OPENDART adapter missing ${token}`)
const kisCode=fs.readFileSync('backend/Shared/Providers/KisMarketDataAdapter.cs','utf8')
for(const token of ['HttpMethod.Get','FHKST01010100','authorization','appkey','appsecret','tr_id'])if(!kisCode.includes(token))errors.push(`KIS adapter missing ${token}`)
if(/HttpMethod\.Post/.test(kisCode))errors.push('KIS market-data adapter must not contain POST trading calls')
for(const f of ['KrxOpenApiAdapter.cs','OpenDartAdapter.cs','KisAccessTokenProvider.cs','KisMarketDataAdapter.cs']){
const code=fs.readFileSync(`backend/Shared/Providers/${f}`,'utf8')
if(/Console\.Write/i.test(code)) errors.push(`${f}: console logging is forbidden in provider adapters`)
if(/LogInformation\([^)]*(appsecret|AuthKey|access_token)/i.test(code)) errors.push(`${f}: possible secret logging`)
}
for(const f of ['tests/fixtures/providers/opendart-no-data.json','tests/fixtures/providers/opendart-rate-limit.json','tests/fixtures/providers/kis-current-price-success.json','backend/tests/Providers/KrxProviderPolicyTests.cs','backend/tests/Providers/OpenDartProviderPolicyTests.cs','backend/tests/Providers/KisProviderPolicyTests.cs'])if(!fs.existsSync(f))errors.push(`missing provider test artifact ${f}`)
if(errors.length){console.error('provider governance FAIL');for(const e of errors)console.error(`- ${e}`);process.exit(1)}
console.log(`provider governance PASS: providers=${src.providers.length}, official-only sources, read-only boundary, secret isolation`)