fix: allow inline page scripts in MindSpace public page CSP
Collect sha256 hashes from all inline scripts in published HTML so agent-generated fade-in animations are not blocked while keeping the share-button CSP model. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,6 +1,22 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { decorateMindSpacePublishedHtml, handleMindSpaceLongImageDownload } from './mindspace-public-delivery.mjs';
|
||||
import {
|
||||
collectInlineScriptHashes,
|
||||
decorateMindSpacePublishedHtml,
|
||||
handleMindSpaceLongImageDownload,
|
||||
} from './mindspace-public-delivery.mjs';
|
||||
|
||||
test('collectInlineScriptHashes returns sha256 hashes for inline scripts only', () => {
|
||||
const html = `<html><body>
|
||||
<script>console.log('page')</script>
|
||||
<script src="https://cdn.example/app.js"></script>
|
||||
<script>console.log('share')</script>
|
||||
</body></html>`;
|
||||
const hashes = collectInlineScriptHashes(html);
|
||||
assert.equal(hashes.length, 2);
|
||||
assert.match(hashes[0], /^[A-Za-z0-9+/]+=*$/);
|
||||
assert.notEqual(hashes[0], hashes[1]);
|
||||
});
|
||||
|
||||
test('handleMindSpaceLongImageDownload renders and downloads when requested', async () => {
|
||||
const calls = [];
|
||||
@@ -79,8 +95,10 @@ test('handleMindSpaceLongImageDownload falls back to local html path without pag
|
||||
});
|
||||
|
||||
test('decorateMindSpacePublishedHtml returns decorated html and csp', () => {
|
||||
const pageScript = "document.body.dataset.ready='1'";
|
||||
const shareScript = "document.body.dataset.share='1'";
|
||||
const result = decorateMindSpacePublishedHtml({
|
||||
html: '<html><body>demo</body></html>',
|
||||
html: `<html><body><script>${pageScript}</script>demo</body></html>`,
|
||||
embed: false,
|
||||
context: {
|
||||
origin: 'https://mm.tkmind.cn',
|
||||
@@ -92,16 +110,23 @@ test('decorateMindSpacePublishedHtml returns decorated html and csp', () => {
|
||||
preparePublicationHtmlForEmbed: (value) => value,
|
||||
injectOgTags: (value, meta) => `${value}<!--og:${meta.pageUrl}-->`,
|
||||
injectWechatShareBridge: (value, meta) => `${value}<!--wechat:${meta.pageUrl}-->`,
|
||||
injectPublicFileShareButton: (value) => ({ html: `${value}<!--share-->`, scriptHashes: ['abc'] }),
|
||||
injectPublicFileShareButton: (value) => ({
|
||||
html: value.replace('</body>', `<script>${shareScript}</script></body>`),
|
||||
scriptHashes: ['ignored'],
|
||||
}),
|
||||
publishedPageCsp: (_value, options) => JSON.stringify(options),
|
||||
isWechatUserAgent: () => true,
|
||||
});
|
||||
|
||||
assert.match(result.html, /og:/);
|
||||
assert.match(result.html, /wechat:/);
|
||||
assert.match(result.html, /share/);
|
||||
assert.match(result.html, /dataset\.share/);
|
||||
assert.equal(result.allowEmbedFrame, false);
|
||||
assert.equal(result.csp, JSON.stringify({ embed: false, wechatShare: true, scriptHashes: ['abc'] }));
|
||||
const options = JSON.parse(result.csp);
|
||||
assert.equal(options.embed, false);
|
||||
assert.equal(options.wechatShare, true);
|
||||
assert.deepEqual(options.scriptHashes, collectInlineScriptHashes(result.html));
|
||||
assert.equal(options.scriptHashes.length, 2);
|
||||
});
|
||||
|
||||
test('decorateMindSpacePublishedHtml supports embed mode without share injection', () => {
|
||||
|
||||
Reference in New Issue
Block a user