feat:日志敏感信息过滤
build / task (16.x, npm, macOS-latest) (push) Canceled after 0s
build / task (16.x, npm, ubuntu-latest) (push) Canceled after 0s
build / task (16.x, npm, windows-latest) (push) Canceled after 0s
build / task (16.x, pnpm, macOS-latest) (push) Canceled after 0s
build / task (16.x, pnpm, ubuntu-latest) (push) Canceled after 0s
build / task (16.x, pnpm, windows-latest) (push) Canceled after 0s
build / task (16.x, yarn, macOS-latest) (push) Canceled after 0s
build / task (16.x, yarn, ubuntu-latest) (push) Canceled after 0s
build / task (16.x, yarn, windows-latest) (push) Canceled after 0s
build / task (18.x, npm, macOS-latest) (push) Canceled after 0s
build / task (18.x, npm, ubuntu-latest) (push) Canceled after 0s
build / task (18.x, npm, windows-latest) (push) Canceled after 0s
build / task (18.x, pnpm, macOS-latest) (push) Canceled after 0s
build / task (18.x, pnpm, ubuntu-latest) (push) Canceled after 0s
build / task (18.x, pnpm, windows-latest) (push) Canceled after 0s
build / task (18.x, yarn, macOS-latest) (push) Canceled after 0s
build / task (18.x, yarn, ubuntu-latest) (push) Canceled after 0s
build / task (18.x, yarn, windows-latest) (push) Canceled after 0s
build / task (20.x, npm, macOS-latest) (push) Canceled after 0s
build / task (20.x, npm, ubuntu-latest) (push) Canceled after 0s
build / task (20.x, npm, windows-latest) (push) Canceled after 0s
build / task (20.x, pnpm, macOS-latest) (push) Canceled after 0s
build / task (20.x, pnpm, ubuntu-latest) (push) Canceled after 0s
build / task (20.x, pnpm, windows-latest) (push) Canceled after 0s
build / task (20.x, yarn, macOS-latest) (push) Canceled after 0s
build / task (20.x, yarn, ubuntu-latest) (push) Canceled after 0s
build / task (20.x, yarn, windows-latest) (push) Canceled after 0s
build / task (16.x, npm, macOS-latest) (push) Canceled after 0s
build / task (16.x, npm, ubuntu-latest) (push) Canceled after 0s
build / task (16.x, npm, windows-latest) (push) Canceled after 0s
build / task (16.x, pnpm, macOS-latest) (push) Canceled after 0s
build / task (16.x, pnpm, ubuntu-latest) (push) Canceled after 0s
build / task (16.x, pnpm, windows-latest) (push) Canceled after 0s
build / task (16.x, yarn, macOS-latest) (push) Canceled after 0s
build / task (16.x, yarn, ubuntu-latest) (push) Canceled after 0s
build / task (16.x, yarn, windows-latest) (push) Canceled after 0s
build / task (18.x, npm, macOS-latest) (push) Canceled after 0s
build / task (18.x, npm, ubuntu-latest) (push) Canceled after 0s
build / task (18.x, npm, windows-latest) (push) Canceled after 0s
build / task (18.x, pnpm, macOS-latest) (push) Canceled after 0s
build / task (18.x, pnpm, ubuntu-latest) (push) Canceled after 0s
build / task (18.x, pnpm, windows-latest) (push) Canceled after 0s
build / task (18.x, yarn, macOS-latest) (push) Canceled after 0s
build / task (18.x, yarn, ubuntu-latest) (push) Canceled after 0s
build / task (18.x, yarn, windows-latest) (push) Canceled after 0s
build / task (20.x, npm, macOS-latest) (push) Canceled after 0s
build / task (20.x, npm, ubuntu-latest) (push) Canceled after 0s
build / task (20.x, npm, windows-latest) (push) Canceled after 0s
build / task (20.x, pnpm, macOS-latest) (push) Canceled after 0s
build / task (20.x, pnpm, ubuntu-latest) (push) Canceled after 0s
build / task (20.x, pnpm, windows-latest) (push) Canceled after 0s
build / task (20.x, yarn, macOS-latest) (push) Canceled after 0s
build / task (20.x, yarn, ubuntu-latest) (push) Canceled after 0s
build / task (20.x, yarn, windows-latest) (push) Canceled after 0s
This commit is contained in:
1 parent
3e744b63f1
commit
1fa2f11057
10 files changed
+362
-14
No files matched your search
@@ -21,10 +21,19 @@ export function fileAccessSignature(file: string, app: string, kid: string, ts:
|
||||
if (!/^[A-Za-z0-9+/]+={0,2}$/.test(key) || CryptoJS.enc.Base64.parse(key).sigBytes < 32) throw new Error('Invalid file access key')
|
||||
return CryptoJS.HmacSHA256(fileAccessCanonical(file, app, kid, ts), CryptoJS.enc.Base64.parse(key)).toString(CryptoJS.enc.Hex)
|
||||
}
|
||||
export function fileUploadCanonical(target: string, app: string, kid: string, ts: number, nonce: string): string {
|
||||
if (!['upload', 'inner-upload', 'qn-upload'].includes(target)) throw new Error('Invalid file upload target')
|
||||
return ['v1', 'oss-file-upload', 'POST', target, app, kid, String(ts), nonce].join('\n')
|
||||
}
|
||||
export function fileUploadSignature(target: string, app: string, kid: string, ts: number, nonce: string, key: string): string {
|
||||
if (!/^[A-Za-z0-9+/]+={0,2}$/.test(key) || CryptoJS.enc.Base64.parse(key).sigBytes < 32) throw new Error('Invalid file upload key')
|
||||
return CryptoJS.HmacSHA256(fileUploadCanonical(target, app, kid, ts, nonce), CryptoJS.enc.Base64.parse(key)).toString(CryptoJS.enc.Hex)
|
||||
}
|
||||
export function createFileAccess(options: FileAccessOptions) {
|
||||
const now = options.now || Date.now
|
||||
let offset = 0, ttl = 1800
|
||||
const cache = new Map<string, { url: string; ts: number }>()
|
||||
let uploadSequence = 0
|
||||
const base = () => (options.baseUrl() || '').replace(/\/+$/, '')
|
||||
const allowed = () => [origin(base()), ...(options.allowedOrigins || []).map(origin)].filter(Boolean)
|
||||
function resolve(source?: string | null): string {
|
||||
@@ -88,6 +97,29 @@ export function createFileAccess(options: FileAccessOptions) {
|
||||
const query = head.slice(at + 1).split('&').filter(p => !FILE_ACCESS_FIELDS.includes(decode(p.split('=')[0])))
|
||||
return head.slice(0, at) + (query.length ? '?' + query.join('&') : '') + hash
|
||||
}
|
||||
function uploadTarget(source: string): string | null {
|
||||
const resolved = resolve(source), host = origin(resolved)
|
||||
if ((host && !allowed().includes(host)) || (/^[a-z][a-z0-9+.-]*:/i.test(resolved) && !host)) return null
|
||||
const path = (host ? resolved.replace(/^https?:\/\/[^/?#]+/i, '') : resolved).split(/[?#]/)[0]
|
||||
const prefix = base().replace(/^https?:\/\/[^/?#]+/i, '')
|
||||
for (const [target, suffix] of [['upload', 'upload'], ['inner-upload', 'inner-upload'], ['qn-upload', 'qn/upload']]) {
|
||||
if (['/sys-file/', '/admin/sys-file/', '/api/sys-file/', '/api/admin/sys-file/', prefix + '/admin/sys-file/', prefix + '/sys-file/']
|
||||
.some(start => path === start + suffix)) return target
|
||||
}
|
||||
return null
|
||||
}
|
||||
function uploadHeaders(source: string, fixedNonce?: string): Record<string, string> {
|
||||
const target = uploadTarget(source)
|
||||
if (!target) return {}
|
||||
const seconds = Math.floor((now() + offset) / 1000)
|
||||
// The nonce is a unique request identifier, not a secret; HMAC provides authorization.
|
||||
const nonce = fixedNonce || Math.floor(now()).toString(36) + '-' + (++uploadSequence).toString(36) + '-'
|
||||
+ Array.from({ length: 4 }, () => Math.floor(Math.random() * 0x100000000).toString(16).padStart(8, '0')).join('')
|
||||
return {
|
||||
'X-File-App-Id': options.appId, 'X-File-Key-Id': options.kid, 'X-File-Timestamp': String(seconds), 'X-File-Nonce': nonce,
|
||||
'X-File-Signature': fileUploadSignature(target, options.appId, options.kid, seconds, nonce, options.key),
|
||||
}
|
||||
}
|
||||
function synchronize(serverTime: number, ttlSeconds = ttl, roundTripMs = 0) {
|
||||
if (!Number.isFinite(serverTime) || serverTime <= 0) return
|
||||
offset = serverTime + roundTripMs / 2 - now()
|
||||
@@ -99,7 +131,7 @@ export function createFileAccess(options: FileAccessOptions) {
|
||||
const parsed = parse(source)
|
||||
if (parsed) cache.delete(parsed.unsigned)
|
||||
}
|
||||
return { url, resolve, strip, protected: (s?: string | null) => !!parse(s), synchronize, invalidate, clear: () => cache.clear(), ttl: () => ttl }
|
||||
return { uploadHeaders, uploadTarget, url, resolve, strip, protected: (s?: string | null) => !!parse(s), synchronize, invalidate, clear: () => cache.clear(), ttl: () => ttl }
|
||||
}
|
||||
|
||||
const unescapeAttribute = (s: string) => s.replace(/&/gi, '&').replace(/"/gi, '"').replace(/&#(?:39|x27);/gi, "'").replace(/&#(x[0-9a-f]+|\d+);/gi, (_, n) => { const code = n[0].toLowerCase() === 'x' ? parseInt(n.slice(1), 16) : Number(n); return code <= 0x10ffff ? String.fromCodePoint(code) : ''; })
|
||||
|
||||
@@ -23,6 +23,7 @@ export const accessFileUrl = (source?: string | null) => isProtectedFile(source)
|
||||
export const fileUrls = (sources: any) => Array.isArray(sources) ? sources.map(accessFileUrl) : []
|
||||
export const originalFileUrl = (source?: string | null) => client.strip(source)
|
||||
export const resolveFileUrl = (source?: string | null) => client.resolve(source)
|
||||
export const fileUploadHeaders = (source: string) => client.uploadHeaders(source)
|
||||
export const fileHtml = (html: string) => rewriteFileHtml(html, accessFileUrl)
|
||||
export const stripFileHtml = (html: string) => rewriteFileHtml(html, originalFileUrl)
|
||||
export const fileNodes = (nodes: any) => rewriteFileNodes(nodes, accessFileUrl)
|
||||
|
||||
@@ -3,6 +3,7 @@ import { Session } from "/@/utils/storage"
|
||||
import { useMessageBox } from "/@/hooks/message"
|
||||
import qs from "qs"
|
||||
import other from "./other"
|
||||
import { fileUploadHeaders } from './file-access'
|
||||
|
||||
/**
|
||||
* 创建并配置一个 Axios 实例对象
|
||||
@@ -48,6 +49,7 @@ service.interceptors.request.use(
|
||||
|
||||
// 自动适配单体和微服务架构不同的URL
|
||||
config.url = other.adaptationUrl(config.url)
|
||||
if (config.method?.toLowerCase() === 'post') Object.assign(config.headers, fileUploadHeaders(axios.getUri(config)))
|
||||
|
||||
// 处理完毕,返回config对象
|
||||
return config
|
||||
|
||||
Reference in new issue
Block a user