dsh-unlag 1.9.45 → 1.9.47

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.
Files changed (2) hide show
  1. package/index.js +44 -4
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { AsyncLocalStorage } from 'node:async_hooks'
2
2
  import { createHash } from 'node:crypto'
3
- import { appendFileSync, existsSync, readFileSync, readdirSync, statSync, watch, writeFileSync } from 'node:fs'
3
+ import { appendFileSync, existsSync, mkdirSync, readFileSync, readdirSync, statSync, watch, writeFileSync } from 'node:fs'
4
4
  import { homedir } from 'node:os'
5
5
  import { join, resolve } from 'node:path'
6
6
  import { Script } from 'node:vm'
@@ -110,6 +110,15 @@ const MAX_SCRIPT_BYTES = 64 * 1024 * 1024
110
110
  * the compile happens under it: what a check keeps per body is the ids it names, never its text.
111
111
  */
112
112
  const MAX_TOTAL_SCRIPT_BYTES = 128 * 1024 * 1024
113
+ /**
114
+ * How large the request that carries the list may be. The list itself is bounded (64 addresses of at
115
+ * most 8 KB, plus JSON escaping), so a bound below that would refuse a sound startup list from a
116
+ * profile with many bundles and report it as an empty one. A body past this bound is not a list this
117
+ * app could boot from, and it is answered as such instead of being read.
118
+ */
119
+ const MAX_LIST_BODY_BYTES = 1024 * 1024
120
+ /** The body reader's answer for a request past its bound: not JSON, and not an empty list either. */
121
+ const TOO_LARGE = Symbol('too-large')
113
122
 
114
123
  /**
115
124
  * The most plugin ids one bundle address may name. A real startup list is far below this; a made-up
@@ -213,6 +222,23 @@ export function oneLine(value, limit = 2000) {
213
222
  return text.length > limit ? `${text.slice(0, limit)}\u2026` : text
214
223
  }
215
224
 
225
+ /** Whether the directory holding the log has already been made, so one attempt per process. */
226
+ let logDirTried = false
227
+ /**
228
+ * The log is the only evidence a failed update leaves behind, and a home directory that never ran
229
+ * this app yet has no directory for it: one attempt per process to make that directory, then the
230
+ * write is retried. Every failure stays silent, because logging must never take a press down.
231
+ */
232
+ function ensureLogDir(target) {
233
+ if (logDirTried) return
234
+ logDirTried = true
235
+ const cut = target.lastIndexOf('/')
236
+ if (cut <= 0) return
237
+ try {
238
+ mkdirSync(target.slice(0, cut), { recursive: true })
239
+ } catch {}
240
+ }
241
+
216
242
  function log(line) {
217
243
  try {
218
244
  const target = logTarget()
@@ -226,7 +252,15 @@ function log(line) {
226
252
  /* a missing or unreadable log is created by the append below */
227
253
  }
228
254
  }
229
- appendFileSync(target, `${new Date().toISOString()} ${oneLine(line)}\n`)
255
+ const text = `${new Date().toISOString()} ${oneLine(line)}\n`
256
+ try {
257
+ appendFileSync(target, text)
258
+ } catch {
259
+ ensureLogDir(target)
260
+ try {
261
+ appendFileSync(target, text)
262
+ } catch {}
263
+ }
230
264
  } catch {}
231
265
  }
232
266
 
@@ -801,7 +835,7 @@ function buttonAllowed(req) {
801
835
  }
802
836
 
803
837
  /** Read one small JSON request body; anything unreadable answers null instead of hanging. */
804
- function readJsonBody(req, limit = 64 * 1024) {
838
+ function readJsonBody(req, limit = MAX_LIST_BODY_BYTES) {
805
839
 
806
840
  return new Promise((resolve) => {
807
841
  let size = 0
@@ -828,7 +862,7 @@ function readJsonBody(req, limit = 64 * 1024) {
828
862
  req.on('data', (chunk) => {
829
863
  size += chunk.length
830
864
  if (size > limit) {
831
- finish(null)
865
+ finish(TOO_LARGE)
832
866
  return
833
867
  }
834
868
  parts.push(chunk)
@@ -1675,6 +1709,12 @@ function serveButton(ctx, controller, config) {
1675
1709
  return sendJson(res, 200, { status: 'blocked', reason: 'list', detail: 'the plugin table is not running' })
1676
1710
  }
1677
1711
  const body = await readJsonBody(req)
1712
+ // A list larger than the bound is refused in its own words: an oversized request is not an
1713
+ // empty list, and a person told their startup list names nothing would look in the wrong place.
1714
+ if (body === TOO_LARGE) {
1715
+ log('a startup check refused the page: size (the startup list is longer than this check reads)')
1716
+ return sendJson(res, 200, { status: 'blocked', reason: 'size', detail: 'the startup list is longer than this check reads' })
1717
+ }
1678
1718
  let verdict = null
1679
1719
  try {
1680
1720
  verdict = await verifyStartup(clientModules, Array.isArray(body?.rows) ? body.rows : [])
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-unlag",
3
- "version": "1.9.45",
3
+ "version": "1.9.47",
4
4
  "type": "module",
5
5
  "description": "A press applies plugin edits right away and brings a laggy interface back; automatic reloading stays off unless you turn it on, so the press is the only trigger, and nothing restarts.",
6
6
  "license": "MIT",