Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/2447-room-share-brace-in-text.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
A chat line that carries a brace — `if (x) { y()` or a lone `}` — reaches the room again instead of being dropped: the room-share parser counts braces outside JSON strings only, so one unbalanced brace no longer ends the envelope early.
13 changes: 12 additions & 1 deletion core/src/shared/room-share.js
Original file line number Diff line number Diff line change
Expand Up @@ -255,11 +255,22 @@ export function parseRoomShare(text) {
if (at < 0) return undefined
const start = raw.indexOf('{', at)
if (start < 0) return undefined
// #2447: braces inside a JSON string are the sender's characters, not structure. One unbalanced
// brace in a chat line used to close the envelope early and drop the whole line.
let depth = 0
let end = -1
let inString = false
let escaped = false
for (let i = start; i < raw.length; i += 1) {
const ch = raw[i]
if (ch === '{') depth += 1
if (inString) {
if (escaped) escaped = false
else if (ch === '\\') escaped = true
else if (ch === '"') inString = false
continue
}
if (ch === '"') inString = true
else if (ch === '{') depth += 1
else if (ch === '}') {
depth -= 1
if (depth === 0) { end = i; break }
Expand Down
34 changes: 34 additions & 0 deletions core/test/room-share-brace-in-text.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// A brace in a chat line is a character someone typed, not structure (#2447): `parseRoomShare`
// finds the end of the JSON envelope by counting braces, and it must not count the ones inside the
// strings `encodeRoomShare` writes — otherwise a single unbalanced brace drops the whole line.
import assert from 'node:assert/strict'
import { test } from 'node:test'
import { encodeRoomShare, parseRoomShare } from '../src/shared/room-share.js'

const line = (text) => encodeRoomShare({ op: 'line', roomId: 'r1', seq: 7, who: 'Alice', text })

test('an open brace in a line does not end the envelope early', () => {
const text = 'if (x) { y()'
const parsed = parseRoomShare(line(text))
assert.ok(parsed, 'the envelope was dropped')
assert.equal(parsed.text, text)
})

test('a close brace in a line does not end the envelope early', () => {
const text = 'so the closing brace is }'
const parsed = parseRoomShare(line(text))
assert.ok(parsed, 'the envelope was dropped')
assert.equal(parsed.text, text)
})

test('a brace inside a quoted word, and an escaped quote, both stay text', () => {
const text = 'he said "{" and then "}" and \\ back'
const parsed = parseRoomShare(line(text))
assert.ok(parsed, 'the envelope was dropped')
assert.equal(parsed.text, text)
})

test('an envelope that really is cut short is still refused (control)', () => {
const text = line('hello there')
assert.equal(parseRoomShare(text.slice(0, text.length - 3)), undefined)
})
13 changes: 12 additions & 1 deletion patches/ds-bot/files/src/shared/room-share.js
Original file line number Diff line number Diff line change
Expand Up @@ -255,11 +255,22 @@ export function parseRoomShare(text) {
if (at < 0) return undefined
const start = raw.indexOf('{', at)
if (start < 0) return undefined
// #2447: braces inside a JSON string are the sender's characters, not structure. One unbalanced
// brace in a chat line used to close the envelope early and drop the whole line.
let depth = 0
let end = -1
let inString = false
let escaped = false
for (let i = start; i < raw.length; i += 1) {
const ch = raw[i]
if (ch === '{') depth += 1
if (inString) {
if (escaped) escaped = false
else if (ch === '\\') escaped = true
else if (ch === '"') inString = false
continue
}
if (ch === '"') inString = true
else if (ch === '{') depth += 1
else if (ch === '}') {
depth -= 1
if (depth === 0) { end = i; break }
Expand Down
34 changes: 34 additions & 0 deletions patches/ds-bot/files/test/room-share-brace-in-text.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// A brace in a chat line is a character someone typed, not structure (#2447): `parseRoomShare`
// finds the end of the JSON envelope by counting braces, and it must not count the ones inside the
// strings `encodeRoomShare` writes — otherwise a single unbalanced brace drops the whole line.
import assert from 'node:assert/strict'
import { test } from 'node:test'
import { encodeRoomShare, parseRoomShare } from '../src/shared/room-share.js'

const line = (text) => encodeRoomShare({ op: 'line', roomId: 'r1', seq: 7, who: 'Alice', text })

test('an open brace in a line does not end the envelope early', () => {
const text = 'if (x) { y()'
const parsed = parseRoomShare(line(text))
assert.ok(parsed, 'the envelope was dropped')
assert.equal(parsed.text, text)
})

test('a close brace in a line does not end the envelope early', () => {
const text = 'so the closing brace is }'
const parsed = parseRoomShare(line(text))
assert.ok(parsed, 'the envelope was dropped')
assert.equal(parsed.text, text)
})

test('a brace inside a quoted word, and an escaped quote, both stay text', () => {
const text = 'he said "{" and then "}" and \\ back'
const parsed = parseRoomShare(line(text))
assert.ok(parsed, 'the envelope was dropped')
assert.equal(parsed.text, text)
})

test('an envelope that really is cut short is still refused (control)', () => {
const text = line('hello there')
assert.equal(parseRoomShare(text.slice(0, text.length - 3)), undefined)
})
Loading