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

This commit is contained in:
dzc committed 2026-10-29 15:46:24 +08:00
1 parent 3e744b63f1
commit 1fa2f11057
10 files changed
+362 -14

No files matched your search

+33 -1
View File
@@ -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(/&amp;/gi, '&').replace(/&quot;/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) : ''; })
+1
View File
@@ -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)
+2
View File
@@ -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