Files
yaklang-chrome-extension/src/features/authorization-testing/baseline-transform.ts
T

78 lines
2.8 KiB
TypeScript

import type {
BrowserAuthorizationBaseline,
BrowserTransformProfile,
} from '@/types/models';
import { ExtensionError } from '@/shared/errors';
const DYNAMIC_FIELD_CATEGORIES = new Set(['signature', 'nonce', 'timestamp', 'csrf']);
function normalizedTransformDestination(destination: string): string {
const trimmed = destination.trim();
if (trimmed.toLowerCase().startsWith('header.')) {
return `header.${trimmed.slice(7).trim().toLowerCase()}`;
}
return trimmed;
}
export function authorizationDynamicTransformDestinations(
baseline: BrowserAuthorizationBaseline,
profile: BrowserTransformProfile,
): string[] {
if (!profile.enabled || !profile.request.enabled) {
throw new ExtensionError('authorization_transform_unavailable', '所选明文网关未启用请求转换');
}
if (profile.recovery && profile.recovery.state !== 'ready') {
throw new ExtensionError('authorization_transform_stale', '所选明文网关正在等待文档恢复或重新验证');
}
const dynamicFields = new Map(
baseline.request.fields
.filter((field) => DYNAMIC_FIELD_CATEGORIES.has(field.category))
.map((field) => [
normalizedTransformDestination(field.path),
field,
]),
);
const required = [...dynamicFields.keys()].filter((path) => {
const field = dynamicFields.get(path);
return field?.category === 'signature'
|| field?.category === 'nonce'
|| field?.category === 'timestamp';
});
if (!required.length) {
throw new ExtensionError('authorization_transform_unnecessary', '当前授权基线没有需要动态重算的签名、Nonce 或时间字段');
}
const destinations = profile.request.nodes
.filter((node) => node.kind === 'output.write')
.map((node) => normalizedTransformDestination(node.destination));
if (!destinations.length) {
throw new ExtensionError('authorization_transform_invalid', '所选明文网关没有请求输出节点');
}
for (const destination of destinations) {
if (
destination === 'body'
|| destination.startsWith('body.')
|| (!destination.startsWith('header.') && !destination.startsWith('query.'))
) {
throw new ExtensionError(
'authorization_transform_unsupported',
'首批授权动态重算只接受 Header/Query 签名字段;Body 加密 envelope 需要逻辑明文绑定',
);
}
if (!dynamicFields.has(destination)) {
throw new ExtensionError(
'authorization_transform_invalid',
`明文网关输出未对应基线中的动态字段: ${destination}`,
);
}
}
const output = [...new Set(destinations)];
const missing = required.find((path) => !output.includes(path));
if (missing) {
throw new ExtensionError(
'authorization_transform_incomplete',
`明文网关尚未覆盖动态字段: ${missing}`,
);
}
return output.sort();
}