kind 别名表查表加 Object.hasOwn 守卫:原型键不再被误当成别名
- `canonicalGameCreationAppAssetKind`(TS)此前用对象字面量直接下标查别名表:`kind` 是外部输入,`constructor` / `__proto__` / `toString` / `valueOf` / `hasOwnProperty` 会命中 `Object.prototype` 上的成员,被当成 canonical kind 返回(`__proto__` 返回原型对象本身);Rust 侧是 `match` 字面量,只会落 `image` 兜底,同一输入两侧分叉 - 改用 `Object.hasOwn(GAME_CREATION_APP_LEGACY_ASSET_KINDS, normalized)` 先确认命中的是表自己的键,再取值;`noUncheckedIndexedAccess` 下仍保留一次取值后的 truthy 判定 - 补断言(无断言的边界修法下次会被改回去):`gameCreationApp.test.ts` 对 `constructor` / `__proto__` / `toString` / `valueOf` / `hasOwnProperty` 逐条断言 `canonicalGameCreationAppAssetKind` 落 `image`、`gameCreationAppAssetCategoryForKind` 落 `unclassified` - 跨语言对照:`assetKindCanonicalMapping.test.ts` 的 `decided` 表新增 `constructor` 与 `__proto__` 两行(`image` / `unclassified`),与 TS 同一份口径对照 - Rust 侧同步断言:`game_creation_app.rs` 的 `asset_category_mapping_covers_every_canonical_kind` 补 `constructor` / `__proto__` / `toString` / `valueOf` 必须落 `image` 与 `unclassified`,把「两侧一致」这件事写进两侧各自的用例 - 边界范围:只收口「原型成员被当成别名」这一种极端输入;`kind` 的正常词表、大小写口径与读时自愈规则不变,manifest 字段构成与顺序不变
This commit is contained in:
@@ -222,6 +222,10 @@ describe('真机出现的 kind 归类口径', () => {
|
||||
// 现役写入侧的大写字面量与字体 kind,两者都必须落进明确栏目。
|
||||
{ kind: 'UI', canonical: 'ui-design', category: 'ui-interaction' },
|
||||
{ kind: 'font', canonical: 'document', category: 'document' },
|
||||
// `Object.prototype` 上的键不是别名:TS 侧必须用 `Object.hasOwn` 挡住对象字面量的
|
||||
// 原型命中,Rust 侧 match 字面量本来就落 `image`;这类极端输入两侧也必须一致。
|
||||
{ kind: 'constructor', canonical: 'image', category: 'unclassified' },
|
||||
{ kind: '__proto__', canonical: 'image', category: 'unclassified' },
|
||||
];
|
||||
|
||||
test.each(decided)(
|
||||
|
||||
@@ -753,6 +753,25 @@ describe('AI 游戏创作 App 共享契约', () => {
|
||||
// 字体资产的现役写入侧写的是 `font`。
|
||||
expect(canonicalGameCreationAppAssetKind('font')).toBe('document');
|
||||
expect(canonicalGameCreationAppAssetKind('FONT')).toBe('document');
|
||||
/**
|
||||
* `Object.prototype` 上的键不是别名:别名表是对象字面量,直接下标取值会拿到原型上的
|
||||
* 函数/对象(truthy)并被当成 canonical kind 返回;Rust 侧是 `match` 字面量,只会落
|
||||
* `image`。必须用 `Object.hasOwn` 挡掉,两侧对这类输入的归类才一致
|
||||
* (`image → unclassified`)。跨语言对照另见 `assetKindCanonicalMapping.test.ts` 的
|
||||
* `decided` 表。
|
||||
*/
|
||||
for (const prototypeKey of [
|
||||
'constructor',
|
||||
'__proto__',
|
||||
'toString',
|
||||
'valueOf',
|
||||
'hasOwnProperty',
|
||||
]) {
|
||||
expect(canonicalGameCreationAppAssetKind(prototypeKey)).toBe('image');
|
||||
expect(gameCreationAppAssetCategoryForKind(prototypeKey)).toBe(
|
||||
'unclassified',
|
||||
);
|
||||
}
|
||||
expect(canonicalGameCreationAppAssetKind('unknown-kind')).toBe('image');
|
||||
});
|
||||
|
||||
|
||||
@@ -547,8 +547,16 @@ export function canonicalGameCreationAppAssetKind(
|
||||
* 且派生值本身就是 `unclassified`,读时自愈也救不回来。
|
||||
*/
|
||||
const normalized = value.trim().toLowerCase();
|
||||
const legacyKind = GAME_CREATION_APP_LEGACY_ASSET_KINDS[normalized];
|
||||
if (legacyKind) return legacyKind;
|
||||
/**
|
||||
* `kind` 是外部输入,可能正好是 `Object.prototype` 上的键(`constructor` / `__proto__` /
|
||||
* `toString` / `valueOf` …)。别名表是对象字面量,直接下标取值会拿到原型上的函数/对象
|
||||
* (truthy),被当成 canonical kind 返回;Rust 侧是 `match` 字面量,只会落 `image`。
|
||||
* 必须先确认命中的是**表自己的键**,两侧对这类输入的归类才一致(都落 `image → unclassified`)。
|
||||
*/
|
||||
if (Object.hasOwn(GAME_CREATION_APP_LEGACY_ASSET_KINDS, normalized)) {
|
||||
const legacyKind = GAME_CREATION_APP_LEGACY_ASSET_KINDS[normalized];
|
||||
if (legacyKind) return legacyKind;
|
||||
}
|
||||
if (
|
||||
(GAME_CREATION_APP_CANONICAL_ASSET_KINDS as readonly string[]).includes(
|
||||
normalized,
|
||||
|
||||
@@ -2177,6 +2177,20 @@ mod tests {
|
||||
game_creation_app_asset_category_for_kind("font"),
|
||||
GameCreationAppAssetCategory::Document
|
||||
);
|
||||
// `Object.prototype` 上的键不是别名:Rust 侧是 `match` 字面量,本来就落 `image`;
|
||||
// TS 侧别名表是对象字面量,必须用 `Object.hasOwn` 挡住原型命中,两侧才一致。
|
||||
// 这两条与 TS `gameCreationApp.test.ts` / `assetKindCanonicalMapping.test.ts` 成对。
|
||||
for prototype_key in ["constructor", "__proto__", "toString", "valueOf"] {
|
||||
assert_eq!(
|
||||
canonical_game_creation_app_asset_kind(prototype_key),
|
||||
"image",
|
||||
"{prototype_key} 必须落 image 兜底"
|
||||
);
|
||||
assert_eq!(
|
||||
game_creation_app_asset_category_for_kind(prototype_key),
|
||||
GameCreationAppAssetCategory::Unclassified
|
||||
);
|
||||
}
|
||||
assert_eq!(
|
||||
game_creation_app_asset_category_for_kind("unknown-kind"),
|
||||
GameCreationAppAssetCategory::Unclassified
|
||||
|
||||
Reference in New Issue
Block a user