81 lines
2.7 KiB
TypeScript
81 lines
2.7 KiB
TypeScript
import { describe, expect, it, vi } from 'vitest';
|
|
import { isStylesheetExecutionTrusted } from '../src/transform/stylesheetTrust';
|
|
import { runTransformation } from '../src/transform/transformService';
|
|
import {
|
|
TRANSFORM_LIMITS,
|
|
assertTransformInputWithinLimits,
|
|
assertTransformOutputWithinLimits,
|
|
withTransformationTimeout,
|
|
} from '../src/transform/transformLimits';
|
|
|
|
describe('stylesheet execution trust', () => {
|
|
it('requires both the exact SHA-256 and the current edit revision', () => {
|
|
const trust = { hash: 'sha256-a', revision: 4 };
|
|
expect(isStylesheetExecutionTrusted(trust, 'sha256-a', 4)).toBe(true);
|
|
expect(isStylesheetExecutionTrusted(trust, 'sha256-b', 4)).toBe(false);
|
|
expect(isStylesheetExecutionTrusted(trust, 'sha256-a', 5)).toBe(false);
|
|
expect(isStylesheetExecutionTrusted(null, 'sha256-a', 4)).toBe(false);
|
|
});
|
|
|
|
it.each(['saxon-js-dynamic', 'native-xsltprocessor'] as const)(
|
|
'refuses the %s service path when authorization does not match',
|
|
async (engine) => {
|
|
await expect(
|
|
runTransformation(
|
|
{
|
|
engine,
|
|
xmlText: '<root/>',
|
|
xsltText: '<xsl:stylesheet/>',
|
|
},
|
|
{
|
|
trustedStylesheet: { hash: 'not-the-stylesheet-hash', revision: 7 },
|
|
currentRevision: 7,
|
|
}
|
|
)
|
|
).rejects.toThrow(/review and trust this exact stylesheet/i);
|
|
}
|
|
);
|
|
});
|
|
|
|
describe('transformation resource limits', () => {
|
|
it('rejects oversized XML, XSLT, and result strings', () => {
|
|
expect(() =>
|
|
assertTransformInputWithinLimits({
|
|
engine: 'native-xsltprocessor',
|
|
xmlText: 'x'.repeat(TRANSFORM_LIMITS.maxXmlBytes + 1),
|
|
xsltText: '<xsl:stylesheet/>',
|
|
})
|
|
).toThrow(/XML input exceeds/i);
|
|
expect(() =>
|
|
assertTransformInputWithinLimits({
|
|
engine: 'saxon-js-dynamic',
|
|
xmlText: '<root/>',
|
|
xsltText: 'x'.repeat(TRANSFORM_LIMITS.maxXsltBytes + 1),
|
|
})
|
|
).toThrow(/stylesheet exceeds/i);
|
|
expect(() =>
|
|
assertTransformOutputWithinLimits({
|
|
engine: 'saxon-js-dynamic',
|
|
output: 'x'.repeat(TRANSFORM_LIMITS.maxOutputBytes + 1),
|
|
diagnostics: [],
|
|
transformedAt: '2026-01-01T00:00:00.000Z',
|
|
})
|
|
).toThrow(/output exceeds/i);
|
|
});
|
|
|
|
it('rejects an asynchronous execution that exceeds its deadline', async () => {
|
|
vi.useFakeTimers();
|
|
try {
|
|
const result = withTransformationTimeout(
|
|
() => new Promise<never>(() => undefined),
|
|
25
|
|
);
|
|
const rejection = expect(result).rejects.toThrow(/25-millisecond/i);
|
|
await vi.advanceTimersByTimeAsync(25);
|
|
await rejection;
|
|
} finally {
|
|
vi.useRealTimers();
|
|
}
|
|
});
|
|
});
|