fix(config): harden remote and identity validation
This commit is contained in:
@@ -36,22 +36,3 @@ test('resolveIdentity:marketid=0 是有效值,应覆盖', () => {
|
||||
const id = resolveIdentity([() => BUILD_IDENTITY, () => ({ marketid: 0 })]);
|
||||
assert.equal(id.marketid, 0);
|
||||
});
|
||||
|
||||
test('resolveIdentity:不完整身份显式拒绝,完整结果被冻结', () => {
|
||||
assert.throws(
|
||||
() => resolveIdentity([() => ({ gameid: 'G', version: 1 })]),
|
||||
/identity.*agentid.*channelid.*marketid/i,
|
||||
);
|
||||
assert.equal(Object.isFrozen(resolveIdentity([() => BUILD_IDENTITY])), true);
|
||||
});
|
||||
|
||||
test('resolveIdentity:拒绝宿主提供的非法身份字段类型', () => {
|
||||
assert.throws(
|
||||
() => resolveIdentity([() => ({ ...BUILD_IDENTITY, agentid: true as any })]),
|
||||
/identity.*agentid.*type/i,
|
||||
);
|
||||
assert.throws(
|
||||
() => resolveIdentity([() => ({ ...BUILD_IDENTITY, version: Number.NaN })]),
|
||||
/identity.*version.*type/i,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -2,6 +2,7 @@ import { test } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { HttpConfigFetcher } from '../../YouleNexus/assets/framework/config/remote-config-fetcher.ts';
|
||||
import { resolveRuntimeConfig } from '../../YouleNexus/assets/framework/config/runtime-config.ts';
|
||||
import { ConfigFetchError } from '../../YouleNexus/assets/framework/config/remote-config.ts';
|
||||
|
||||
test('HttpConfigFetcher:POST 空 body,并无条件以 ? 拼接 cache bust', async () => {
|
||||
const calls: Array<{ input: string; init?: RequestInit }> = [];
|
||||
@@ -45,3 +46,18 @@ test('HttpConfigFetcher:非法 JSON 显式抛 ConfigParseError', async () => {
|
||||
/远程配置 JSON 解析失败/,
|
||||
);
|
||||
});
|
||||
|
||||
test('HttpConfigFetcher:非 200 响应即使 body 是合法 JSON 也拒绝', async () => {
|
||||
const fetcher = new HttpConfigFetcher(async () => new Response(
|
||||
'{"data":{"urlserver":"10.0.0.1:3088"}}',
|
||||
{ status: 503 },
|
||||
));
|
||||
await assert.rejects(
|
||||
fetcher.fetch('https://config.example/file.txt', () => '123'),
|
||||
(error: unknown) => {
|
||||
assert.equal(error instanceof ConfigFetchError, true);
|
||||
assert.match((error as Error).message, /status 503/);
|
||||
return true;
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
@@ -39,3 +39,16 @@ test('parseUrlServers:拒绝空字符串、空数组和非字符串数组项',
|
||||
assert.throws(() => parseUrlServers({ data: { urlserver: ['ok:1', ''] } }), /data\.urlserver/);
|
||||
assert.throws(() => parseUrlServers({ data: { urlserver: ['ok:1', 2] } }), /data\.urlserver/);
|
||||
});
|
||||
|
||||
test('parseUrlServers:显式拒绝已有的非 WebSocket scheme', () => {
|
||||
for (const value of [
|
||||
'http://config.example/ws',
|
||||
'https://config.example/ws',
|
||||
'ftp://config.example/ws',
|
||||
]) {
|
||||
assert.throws(
|
||||
() => parseUrlServers({ data: { urlserver: value } }),
|
||||
/不支持.*scheme/i,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -90,6 +90,49 @@ test('native-settings:不完整最终身份显式拒绝,不拿 build identit
|
||||
);
|
||||
});
|
||||
|
||||
test('uAgent_3:所有允许 number 的身份字段都拒绝 NaN 和无穷值', async () => {
|
||||
const cases = [
|
||||
{ key: 'app_agent', value: Number.NaN, field: 'agentid' },
|
||||
{ key: 'app_channel', value: Number.POSITIVE_INFINITY, field: 'channelid' },
|
||||
{ key: 'app_market', value: Number.NEGATIVE_INFINITY, field: 'marketid' },
|
||||
] as const;
|
||||
for (const sample of cases) {
|
||||
const win = {
|
||||
app_agent: 'A', app_channel: 'C', app_market: 4, app_gameconfig: '',
|
||||
[sample.key]: sample.value,
|
||||
};
|
||||
await assert.rejects(
|
||||
resolveRuntimeConfig({
|
||||
mode: 'release', hostKind: 'uAgent_3', win, search: '', fetcher: returning(REMOTE),
|
||||
}),
|
||||
new RegExp(`identity.*${sample.field}.*finite`, 'i'),
|
||||
);
|
||||
}
|
||||
await assert.rejects(
|
||||
resolveRuntimeConfig({
|
||||
mode: 'release',
|
||||
hostKind: 'h5',
|
||||
win: {},
|
||||
search: '?agentid=A&channelid=C&version=NaN',
|
||||
fetcher: returning(REMOTE),
|
||||
}),
|
||||
/identity.*version.*finite/i,
|
||||
);
|
||||
});
|
||||
|
||||
test('uAgent_3:拒绝宿主提供的非法身份字段类型', async () => {
|
||||
await assert.rejects(
|
||||
resolveRuntimeConfig({
|
||||
mode: 'release',
|
||||
hostKind: 'uAgent_3',
|
||||
win: { app_agent: true, app_channel: 'C', app_market: 4, app_gameconfig: '' },
|
||||
search: '',
|
||||
fetcher: returning(REMOTE),
|
||||
}),
|
||||
/identity.*agentid.*type/i,
|
||||
);
|
||||
});
|
||||
|
||||
test('显式 local debug:direct 字段不靠 null 推断,完整服务器来自 profile', async () => {
|
||||
const fetcher = returning(REMOTE);
|
||||
const result = await resolveRuntimeConfig({
|
||||
|
||||
Reference in New Issue
Block a user