fix(h5): prevent internal asset host exposure
Some checks failed
CI / verify (push) Has been cancelled
Some checks failed
CI / verify (push) Has been cancelled
Made-with: Proma
This commit is contained in:
@@ -10,6 +10,7 @@ const placeholder = '__REPLACE_WITH_TENCENT_MAP_WEB_KEY__'
|
|||||||
const badPatterns = [
|
const badPatterns = [
|
||||||
'http://1.92.206.90:9000',
|
'http://1.92.206.90:9000',
|
||||||
'1.92.206.90',
|
'1.92.206.90',
|
||||||
|
'172.20.14.21',
|
||||||
'guide.whaoyue.com',
|
'guide.whaoyue.com',
|
||||||
placeholder
|
placeholder
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -100,9 +100,7 @@ export const dataSourceConfig = {
|
|||||||
audioApiTimeoutMs: normalizeTimeout(
|
audioApiTimeoutMs: normalizeTimeout(
|
||||||
import.meta.env.VITE_AUDIO_API_TIMEOUT_MS || import.meta.env.VITE_EXPLAIN_API_TIMEOUT_MS,
|
import.meta.env.VITE_AUDIO_API_TIMEOUT_MS || import.meta.env.VITE_EXPLAIN_API_TIMEOUT_MS,
|
||||||
defaultExplainRequestTimeoutMs
|
defaultExplainRequestTimeoutMs
|
||||||
),
|
)
|
||||||
publicSameOriginAssetHost: import.meta.env.VITE_PUBLIC_SAME_ORIGIN_ASSET_HOST?.trim() || '',
|
|
||||||
publicLegacyAudioHost: import.meta.env.VITE_PUBLIC_LEGACY_AUDIO_HOST?.trim() || ''
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const isSgsSdkMode = () => dataSourceConfig.guideDataMode === 'sdk'
|
export const isSgsSdkMode = () => dataSourceConfig.guideDataMode === 'sdk'
|
||||||
|
|||||||
2
src/env.d.ts
vendored
2
src/env.d.ts
vendored
@@ -25,8 +25,6 @@ interface ImportMetaEnv {
|
|||||||
readonly VITE_SGS_SDK_ORIGIN?: string
|
readonly VITE_SGS_SDK_ORIGIN?: string
|
||||||
readonly VITE_SGS_SDK_TIMEOUT_MS?: string
|
readonly VITE_SGS_SDK_TIMEOUT_MS?: string
|
||||||
readonly VITE_TENCENT_MAP_KEY?: string
|
readonly VITE_TENCENT_MAP_KEY?: string
|
||||||
readonly VITE_PUBLIC_SAME_ORIGIN_ASSET_HOST?: string
|
|
||||||
readonly VITE_PUBLIC_LEGACY_AUDIO_HOST?: string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ImportMeta {
|
interface ImportMeta {
|
||||||
|
|||||||
@@ -4,24 +4,6 @@ import {
|
|||||||
|
|
||||||
const isAbsoluteHttpUrl = (url: string) => /^https?:\/\//i.test(url)
|
const isAbsoluteHttpUrl = (url: string) => /^https?:\/\//i.test(url)
|
||||||
|
|
||||||
const defaultSameOriginAssetHosts = [
|
|
||||||
['1', '92', '206', '90'].join('.')
|
|
||||||
]
|
|
||||||
|
|
||||||
const defaultLegacyAudioHosts = [
|
|
||||||
['47', '120', '48', '148'].join('.')
|
|
||||||
]
|
|
||||||
|
|
||||||
const configuredSameOriginAssetHosts = () => [
|
|
||||||
dataSourceConfig.publicSameOriginAssetHost,
|
|
||||||
...defaultSameOriginAssetHosts
|
|
||||||
].filter(Boolean)
|
|
||||||
|
|
||||||
const configuredLegacyAudioHosts = () => [
|
|
||||||
dataSourceConfig.publicLegacyAudioHost,
|
|
||||||
...defaultLegacyAudioHosts
|
|
||||||
].filter(Boolean)
|
|
||||||
|
|
||||||
const resolveSdkMinioProxyBaseUrl = () => {
|
const resolveSdkMinioProxyBaseUrl = () => {
|
||||||
const apiBaseUrl = dataSourceConfig.apiBaseUrl.trim().replace(/\/+$/, '')
|
const apiBaseUrl = dataSourceConfig.apiBaseUrl.trim().replace(/\/+$/, '')
|
||||||
const normalizedApiBaseUrl = apiBaseUrl.endsWith('/app-api')
|
const normalizedApiBaseUrl = apiBaseUrl.endsWith('/app-api')
|
||||||
@@ -50,11 +32,8 @@ export const normalizeSameOriginPublicUrl = (url: string | null | undefined) =>
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const parsed = new URL(value)
|
const parsed = new URL(value)
|
||||||
const isSameServer = configuredSameOriginAssetHosts().includes(parsed.hostname)
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
isSameServer
|
parsed.port === '9000'
|
||||||
&& parsed.port === '9000'
|
|
||||||
&& (
|
&& (
|
||||||
parsed.pathname.startsWith('/museum-assets/')
|
parsed.pathname.startsWith('/museum-assets/')
|
||||||
|| parsed.pathname.startsWith('/tts-audio/')
|
|| parsed.pathname.startsWith('/tts-audio/')
|
||||||
@@ -64,16 +43,14 @@ export const normalizeSameOriginPublicUrl = (url: string | null | undefined) =>
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
isSameServer
|
parsed.port === '48080'
|
||||||
&& parsed.port === '48080'
|
|
||||||
&& parsed.pathname.startsWith('/yudao-server/')
|
&& parsed.pathname.startsWith('/yudao-server/')
|
||||||
) {
|
) {
|
||||||
return `${parsed.pathname}${parsed.search}${parsed.hash}`
|
return `${parsed.pathname}${parsed.search}${parsed.hash}`
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
configuredLegacyAudioHosts().includes(parsed.hostname)
|
parsed.port === '19000'
|
||||||
&& parsed.port === '19000'
|
|
||||||
&& parsed.pathname.startsWith('/nhm/audio/')
|
&& parsed.pathname.startsWith('/nhm/audio/')
|
||||||
) {
|
) {
|
||||||
return `${parsed.pathname}${parsed.search}${parsed.hash}`
|
return `${parsed.pathname}${parsed.search}${parsed.hash}`
|
||||||
|
|||||||
@@ -12,6 +12,20 @@ describe('normalizeSameOriginPublicUrl', () => {
|
|||||||
)).toBe('/app-api/gis/sdk/minio/museum-assets/guide-final/E2/detail.jpg')
|
)).toBe('/app-api/gis/sdk/minio/museum-assets/guide-final/E2/detail.jpg')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('rewrites legacy absolute media and proxy URLs without embedding their host', () => {
|
||||||
|
expect(normalizeSameOriginPublicUrl(
|
||||||
|
'http://172.20.14.21:9000/museum-assets/guide-final/E2/detail.jpg'
|
||||||
|
)).toBe('/app-api/gis/sdk/minio/museum-assets/guide-final/E2/detail.jpg')
|
||||||
|
|
||||||
|
expect(normalizeSameOriginPublicUrl(
|
||||||
|
'http://172.20.14.21:48080/yudao-server/app-api/gis/guide?stopId=1'
|
||||||
|
)).toBe('/yudao-server/app-api/gis/guide?stopId=1')
|
||||||
|
|
||||||
|
expect(normalizeSameOriginPublicUrl(
|
||||||
|
'http://172.20.14.21:19000/nhm/audio/guide.mp3'
|
||||||
|
)).toBe('/nhm/audio/guide.mp3')
|
||||||
|
})
|
||||||
|
|
||||||
it('keeps ordinary relative paths unchanged', () => {
|
it('keeps ordinary relative paths unchanged', () => {
|
||||||
expect(normalizeSameOriginPublicUrl('/static/placeholder.png'))
|
expect(normalizeSameOriginPublicUrl('/static/placeholder.png'))
|
||||||
.toBe('/static/placeholder.png')
|
.toBe('/static/placeholder.png')
|
||||||
|
|||||||
Reference in New Issue
Block a user