From 2d9446301bdf4d1b239f8e5be2954d2ed74a98ca Mon Sep 17 00:00:00 2001 From: Lilleman auf Larv Date: Wed, 4 Nov 2026 20:22:16 +0100 Subject: [PATCH] =?UTF-8?q?Answer=20security=20round=201:=200=20?= =?UTF-8?q?=F0=9F=94=B4,=201=20=F0=9F=9F=A1=20(0=20misread);=20build=20the?= =?UTF-8?q?=20refusal's=20path=20in=20linear=20time?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/adf/document.test.ts | 11 ++++++++++- src/adf/document.ts | 3 ++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/adf/document.test.ts b/src/adf/document.test.ts index 6f08fe9..a5d2d7d 100644 --- a/src/adf/document.test.ts +++ b/src/adf/document.test.ts @@ -2,7 +2,7 @@ import assert from 'node:assert/strict' import test from 'node:test' import vm from 'node:vm' -import type { AdfDocument } from './document.ts' +import type { AdfDocument, AdfNode } from './document.ts' import type { JsonValue } from '../json-value.ts' import { adfToHtml, adfToLosslessMarkdown, adfToPortableMarkdown } from '../index.ts' import { adfDocumentFault, isAdfDocument, writableContent } from './document.ts' @@ -158,3 +158,12 @@ test('writes a card holding no link mark, and a node type it does not know holdi const marked = { content: [{ attrs: { url: 'https://example.com/roadmap' }, marks: [comment], type: 'inlineCard' }, { marks: [link], type: 'futureInline' }], type: 'paragraph' } for (const write of [adfToHtml, adfToLosslessMarkdown, adfToPortableMarkdown]) assert.equal(write({ content: [marked], type: 'doc', version: 1 }).ok, true) }) + +test('names a link-marked card at any depth by its path alone, in time linear in the depth', () => { + const depth = 100_000 + let node: AdfNode = { attrs: { url: 'https://example.com/' }, marks: [{ attrs: { href: 'https://example.com/' }, type: 'link' }], type: 'inlineCard' } + for (let level = 0; level < depth; level += 1) node = { content: [node], type: 'panel' } + const refused = writableContent({ content: [node], type: 'doc', version: 1 }) + assert.equal(!refused.ok && refused.error.path.length, 2 * (depth + 1)) + assert.deepEqual(!refused.ok && refused.error.path.slice(0, 4), ['content', 0, 'content', 0]) +}) diff --git a/src/adf/document.ts b/src/adf/document.ts index 6d88590..d05df5d 100644 --- a/src/adf/document.ts +++ b/src/adf/document.ts @@ -154,7 +154,8 @@ function refusedShape(content: readonly AdfNode[]): Result | undefined { for (let step = pending.pop(); step !== undefined; step = pending.pop()) { if (step.node.type === 'inlineCard' && nodeMarks(step.node).some(({ type }) => type === 'link')) { const path: (number | string)[] = [] - for (let up: Step | undefined = step; up !== undefined; up = up.parent) path.unshift('content', up.index) + for (let up: Step | undefined = step; up !== undefined; up = up.parent) path.push(up.index, 'content') + path.reverse() return failure('unsupported-node-shape', 'an inlineCard takes no link mark: this one holds one', path) } for (const child of nodeContent(step.node).map((node, index) => ({ index, node, parent: step })).reverse()) pending.push(child)