tosijs-styled-editor 0.5.0 → 0.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -7,6 +7,73 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.5.2] - 2026-09-26
11
+
12
+ ### Security
13
+
14
+ - **0.5.1's fix was incomplete: `reviseWith()` could still emit live HTML.**
15
+ The guard that strips `<` and `>` from `changeAuthor` was applied at the four
16
+ write sites in the component and missed the fifth, `mark()` in
17
+ `src/changes.ts` — which is exactly what `reviseWith()` and the exported
18
+ `applyRevision()` go through. So the LLM-proofreading path, documented public
19
+ API, still broke a display name out of a raw-text element and put an
20
+ `<img onerror>` into `editor.value`. Reproduced end to end through the public
21
+ `value` setter and `reviseWith()`, with no host cooperation beyond wiring
22
+ `changeAuthor.name` to a profile name.
23
+
24
+ The guard now lives in `src/changes.ts` beside the only code that writes those
25
+ attributes, and every writer shares it. **A guard belongs at the layer every
26
+ writer shares, not at the addresses where the bug was first noticed** — which
27
+ is precisely the mistake 0.5.1 made.
28
+
29
+ **Upgrade from 0.5.1 as well as from 0.5.0** if you use `trackChanges` or
30
+ `reviseWith`. `SECURITY.md`'s statement in 0.5.1 was true of typing and
31
+ deletion and false of revision.
32
+
33
+ ## [0.5.1] - 2026-09-26
34
+
35
+ ### Security
36
+
37
+ - **An author's display name could break out of a raw-text element and become
38
+ live HTML.** HTML attribute serialization escapes `&` and `"` but **never
39
+ `<`**, and `style`, `xmp`, `title`, `textarea`, `noembed`, `noframes` and
40
+ `plaintext` re-parse their contents as raw text — so a `changeAuthor.name`
41
+ containing `</style><img src=x onerror=…>`, written into a change mark inside
42
+ one of those elements, terminated the element and the remainder parsed as
43
+ markup. `editor.value` then carried a real `<img onerror>`.
44
+
45
+ Nothing external triggers the re-parse: `value` is also the undo stack, so a
46
+ single undo does it in the same session, and `internals.setFormValue` hands it
47
+ to every other reader. Reachable without host cooperation — `tosijs-kilpi`
48
+ drops `style`/`script`/`iframe` but keeps `xmp`/`textarea`/`title`/`noembed`/
49
+ `noframes`/`plaintext`, so a collaborator can paste one — and the host need
50
+ only have wired `changeAuthor.name` to a profile name, which is what that
51
+ field is for.
52
+
53
+ `<` and `>` are now stripped from `changeAuthor.id` and `.name` at every write
54
+ site. Stripping rather than escaping: no escape survives attribute
55
+ serialization into a raw-text element. Affects 0.5.0 only, and only with
56
+ `trackChanges` enabled.
57
+
58
+ **Still true, and not a vulnerability:** tracked edits inside a raw-text
59
+ element are a bad idea regardless — a caret marker left in a `<style>` block
60
+ round-trips into literal CSS text with no attacker involved. Keeping the
61
+ editor's own marks out of those elements is tracked in `TODO.md`. See
62
+ `SECURITY.md`.
63
+
64
+ ### Changed
65
+
66
+ - The build no longer passes `--incremental` to `tsc`, so no
67
+ `dist/tsconfig.tsbuildinfo` is produced. An incremental build into a wiped
68
+ `dist/` can emit nothing at all the second time, which disqualifies a build
69
+ that has to reproduce for the publish workflow.
70
+
71
+ ### Added
72
+
73
+ - `.github/workflows/publish.yml` — OIDC trusted publishing with npm staged
74
+ publishing. CI can only *stage*; the maintainer's 2FA approval on npmjs.com is
75
+ what publishes, and it works from a phone.
76
+
10
77
  ## [0.5.0] - 2026-09-21
11
78
 
12
79
  ### Added
package/SECURITY.md CHANGED
@@ -30,3 +30,32 @@ and we will find a private channel.
30
30
  content are inside your trust boundary and are not filtered.
31
31
  - **documents stored before 0.4.4**, which may already contain a pasted payload.
32
32
  Setting `value` does not filter, so sanitize your corpus as part of upgrading.
33
+
34
+ ## `changeAuthor` is written into the document
35
+
36
+ `editor.changeAuthor` (`id` and `name`) is serialized verbatim into
37
+ `data-author` / `data-author-name` on every change mark, so it reaches
38
+ `editor.value`, the form value and every undo snapshot. It is host-supplied and
39
+ inside your trust boundary — but "the host wired it to a profile name" is the
40
+ ordinary case, so the editor does not trust it: `<` and `>` are stripped at the
41
+ seam (fully fixed in 0.5.2 — 0.5.0 is affected, and **0.5.1 is still affected
42
+ through `reviseWith()`**, whose write site the first fix missed).
43
+
44
+ They have to be stripped rather than escaped. HTML attribute serialization
45
+ escapes `&` and `"` and **never `<`**, and `style`, `xmp`, `title`, `textarea`,
46
+ `noembed`, `noframes` and `plaintext` re-parse their contents as raw text — so a
47
+ `</style>` inside an attribute value written into one of those terminates the
48
+ element and everything after it parses as markup. Nothing external triggers the
49
+ re-parse: `value` is also the undo stack, so a single undo does it in the same
50
+ session.
51
+
52
+ Two consequences worth knowing:
53
+
54
+ - **A display name is not a place to put markup**, and it comes back with its
55
+ angle brackets removed rather than escaped. There is no escape that survives
56
+ attribute serialization into a raw-text element.
57
+ - **Tracked edits inside a raw-text element remain a bad idea.** With no
58
+ attacker at all, a caret marker left inside a `<style>` block round-trips into
59
+ literal CSS text. Keeping the editor's own marks out of those elements is
60
+ tracked as an open item; until it lands, do not point this editor at documents
61
+ whose `<style>`/`<xmp>` content is meant to survive editing.
package/dist/changes.d.ts CHANGED
@@ -63,6 +63,26 @@ export declare class TosiIns extends HTMLElement {
63
63
  export declare class TosiDel extends HTMLElement {
64
64
  }
65
65
  export declare function defineChanges(): void;
66
+ /**
67
+ * An attribute value that cannot break out of the element it is written into.
68
+ *
69
+ * HTML attribute serialization escapes `&` and `"` and **never `<`**. Inside a
70
+ * raw-text or RCDATA element — `style`, `xmp`, `title`, `textarea`, `noembed`,
71
+ * `noframes`, `plaintext` — the contents re-parse as text, so a `</style>` in
72
+ * an attribute value terminates the element and everything after it parses as
73
+ * markup, and `editor.value` then carries live HTML.
74
+ *
75
+ * This lives HERE, next to the only code that writes these attributes, because
76
+ * the first version of the fix lived in the component and covered four of the
77
+ * five write sites. The fifth was `mark()` below — on the public `reviseWith()`
78
+ * path — so 0.5.1 shipped as the fix for this and was still exploitable
79
+ * through it. A guard belongs at the layer every writer shares, not at the
80
+ * addresses where the bug was first noticed.
81
+ *
82
+ * Stripping, not escaping: no escape survives attribute serialization into a
83
+ * raw-text element.
84
+ */
85
+ export declare function safeAttributeValue(value: string): string;
66
86
  /** Every tracked change in a root, in document order. */
67
87
  export declare function changesIn(root: Element): TrackedChange[];
68
88
  /**