40 lines
4.7 KiB
JavaScript
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`)
|