const {test} = require('node:test') const assert = require('node:assert/strict') const fs = require('node:fs') const path = require('node:path') const Module = require('node:module') const ts = require('typescript') const root = path.resolve(__dirname, '..') const folder = fs.existsSync(path.join(root,'src/utils/file-access-core.ts')) ? 'src/utils' : 'src/sheep/utils' const corePath = path.join(root,folder,'file-access-core.ts') const compile = source => ts.transpileModule(source,{compilerOptions:{module:ts.ModuleKind.CommonJS,target:ts.ScriptTarget.ES2020}}).outputText const core = new Module(corePath,module); core.filename=corePath; core.paths=Module._nodeModulePaths(path.dirname(corePath)) core._compile(compile(fs.readFileSync(corePath,'utf8')),corePath) const vectors=JSON.parse(fs.readFileSync(path.join(root,'test-vectors/file-upload.json'),'utf8')) for(const v of vectors) test('upload signature vector: '+v.target,()=>{ assert.equal(core.exports.fileUploadCanonical(v.target,v.appId,v.kid,v.ts,v.nonce),v.canonical) assert.equal(core.exports.fileUploadSignature(v.target,v.appId,v.kid,v.ts,v.nonce,v.key),v.sign) }) test('upload credentials bind the purpose and target, renew per request and never leak to other hosts or imports',()=>{ let time=1793232000000 const client=core.exports.createFileAccess({appId:'admin',kid:'v1',key:vectors[0].key,baseUrl:()=> 'https://files.example/gateway/api',now:()=>time,allowedOrigins:['https://legacy.example']}) for(const [source,target] of [['/api/admin/sys-file/upload','upload'],['/sys-file/inner-upload','inner-upload'],['https://legacy.example/admin/sys-file/qn/upload','qn-upload'],['https://files.example/gateway/api/admin/sys-file/upload','upload']]){ const headers=client.uploadHeaders(source,vectors[0].nonce) assert.equal(headers['X-File-Signature'],core.exports.fileUploadSignature(target,'admin','v1',1793232000,vectors[0].nonce,vectors[0].key)) assert.equal(Object.values(headers).includes(vectors[0].key),false) } const first=client.uploadHeaders('/admin/sys-file/upload'), second=client.uploadHeaders('/admin/sys-file/upload') assert.notEqual(first['X-File-Nonce'],second['X-File-Nonce']) assert.notEqual(first['X-File-Signature'],second['X-File-Signature']) assert.equal(first['X-File-Timestamp'],'1793232000') client.synchronize(time+86400000,1800,0) assert.equal(client.uploadHeaders('/admin/sys-file/upload')['X-File-Timestamp'],'1793318400') for(const source of ['https://outside.example/admin/sys-file/upload','https://files.example.evil/admin/sys-file/upload','/admin/sys-file/oss/file?fileName=x','/admin/user/import','data:upload','blob:upload']){ assert.deepEqual(client.uploadHeaders(source),{}) } assert.notEqual(core.exports.fileAccessSignature('a.jpg','admin','v1',1793232000,vectors[0].key),first['X-File-Signature']) }) function runtime(){ const filename=path.join(root,folder,'file-access.ts') const env={VITE_FILE_ACCESS_APP_ID:'admin',VITE_FILE_ACCESS_KEY_ID:'v1',VITE_FILE_ACCESS_KEY:vectors[0].key,VITE_IMAGE_BASE_PATH:'https://files.example/api',VITE_IMAGE_DEV_PATH:'https://files.example/api',VITE_APP_DEV_SERVICE:'https://files.example/api',VITE_APP_PROD_IMAGE:'https://files.example/api',VITE_APP_PROD_SERVICE:'https://files.example/api'} const loaded=new Module(filename,module);loaded.filename=filename;loaded.paths=core.paths loaded.require=name=>name==='./file-access-core'?core.exports:name==='./app'?{isDevEnv:()=>true}:core.require(name) loaded._compile(compile(fs.readFileSync(filename,'utf8').replace(/import\.meta\.env/g,'('+JSON.stringify(env)+')')),filename) return loaded.exports } if(folder==='src/utils') test('the real Axios interceptor signs each file upload and preserves login and tenant headers',()=>{ const api=runtime();let onRequest const axios={getUri:require('axios').getUri,create:()=>({interceptors:{request:{use(fn){onRequest=fn}},response:{use(){}}}})} const filename=path.join(root,'src/utils/request.ts');const loaded=new Module(filename,module);loaded.filename=filename;loaded.paths=core.paths loaded.require=name=>{ if(name==='axios')return {default:axios} if(name==='./file-access')return api if(name==='./other')return {default:{adaptationUrl:url=>url}} if(name==='/@/utils/storage')return {Session:{getToken:()=> 'user-token',getTenant:()=> '42'}} if(name==='/@/hooks/message')return {} return core.require(name) } loaded._compile(compile(fs.readFileSync(filename,'utf8').replace(/import\.meta\.env/g,'({VITE_API_URL:"/api"})')),filename) const first=onRequest({method:'post',url:'/admin/sys-file/upload',headers:{},data:{file:'unchanged'}}) const second=onRequest({method:'post',url:'/admin/sys-file/upload',headers:{},data:{file:'unchanged'}}) assert.equal(first.headers.Authorization,'Bearer user-token');assert.equal(first.headers['TENANT-ID'],'42') assert.ok(first.headers['X-File-Signature']);assert.notEqual(first.headers['X-File-Nonce'],second.headers['X-File-Nonce']) assert.deepEqual(first.data,{file:'unchanged'}) const other=onRequest({method:'post',url:'https://outside.example/admin/sys-file/upload',headers:{}}) assert.equal(other.headers['X-File-Signature'],undefined) const otherBase=onRequest({method:'post',baseURL:'https://outside.example/api',url:'/admin/sys-file/upload',headers:{}}) assert.equal(otherBase.headers['X-File-Signature'],undefined) }) else test('the actual uni upload interceptor signs photo, video and rich text calls without changing form data',()=>{ const previous=global.uni;let interceptor global.uni={addInterceptor(name,hook){assert.equal(name,'uploadFile');interceptor=hook},onNetworkStatusChange(){}} try{ const api=runtime();api.installFileAccess({config:{globalProperties:{}},mixin(){}}) const first={url:'/api/admin/sys-file/upload',header:{Authorization:'Bearer user','TENANT-ID':'42'},formData:{dir:'photos'},filePath:'local-photo.png'} const second={...first,header:{...first.header}} interceptor.invoke(first);interceptor.invoke(second) assert.ok(first.header['X-File-Signature']);assert.notEqual(first.header['X-File-Nonce'],second.header['X-File-Nonce']) assert.equal(first.header.Authorization,'Bearer user');assert.equal(first.header['TENANT-ID'],'42') assert.deepEqual(first.formData,{dir:'photos'});assert.equal(first.filePath,'local-photo.png') const other={url:'https://outside.example/upload',header:{Authorization:'other'}};interceptor.invoke(other) assert.deepEqual(other.header,{Authorization:'other'}) }finally{global.uni=previous} })