forgetrail 0.4.8 → 0.4.10
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/WORKFLOW.md
CHANGED
|
@@ -525,6 +525,7 @@ FilePress publishes Markdown. It does not replace Default-A for an interactive a
|
|
|
525
525
|
- Check for silent failures (functions that catch errors and return empty results)
|
|
526
526
|
- Consolidate documentation (eliminate duplicates, update cross-references)
|
|
527
527
|
- Add all security and code quality findings to TODO.md with P0/P1/P2 priority tiers
|
|
528
|
+
- After GitHub Advanced Security is first enabled, triage Dependabot High with lockfile `overrides` (do not major-upgrade an EOL framework for a nested pin) and CodeQL High with the known hygiene patterns: suffix-safe host match, escape `\\` first, strip trailing slashes without `/\/+$/`. Dismiss leftover Vite 4 / SvelteKit 1 / Svelte 3 / devalue 4 / undici 5 alerts (no patch on that major; local/dev-only). Do not file GHSA/CVE for scanner findings unless there is a real remote exploit. Search lessons for “Dependabot”, “sharp”, “fast-uri”, “Vite 4”, and “CodeQL”.
|
|
528
529
|
|
|
529
530
|
**Artifacts to create:**
|
|
530
531
|
|
|
@@ -1097,6 +1097,7 @@ These are the failures ForgeTrail sees most often. The agent should re-read this
|
|
|
1097
1097
|
- **Env secrets in the repo.** Only `.env.example` is committed. Real `.env` is in `.gitignore`. Check before every commit.
|
|
1098
1098
|
- **Assuming `.env` is loaded without code.** Node/TS entrypoints (`tsx`, `node`, Hono, workers) do **not** read `.env` unless you call **`dotenv`** (or equivalent) at startup — see §4.2.1. Vite loads env for `vite.config.*`, but monorepo layouts need **merged `loadEnv`** from repo root + package with paths derived from **`import.meta.url`**, not `process.cwd()` alone.
|
|
1099
1099
|
- **pnpm ignored native build scripts.** `better-sqlite3` and similar may install without `prebuild-install` / `node-gyp` on pnpm v9+ — see §4.1 preflight step 5 (`onlyBuiltDependencies` at workspace root).
|
|
1100
|
+
- **Major-upgrading a framework to clear a nested Dependabot High.** Pin the named package with `pnpm.overrides` in the flagged lockfile (`sharp` `0.35.4`, `fast-uri` `3.1.7`, `cookie@<0.7.0` → `0.7.2`, `brace-expansion` `5.0.9`, `launch-editor` `2.14.1`). Leave Vite 4 / SvelteKit 1 where they are unless you meant to take that upgrade. Dismiss leftover Vite 4 / Kit 1 / Svelte 3 / devalue 4 / undici 5 alerts (no patch on that major).
|
|
1100
1101
|
- **Wrong API on a busy port.** A default like **8787** may already serve **another app** (unrelated JSON, 404s that look like your bug). Before debugging routes, **`curl http://127.0.0.1:<PORT>/api/health`** (or your health path). Teach changing **`PORT` + proxy / CORS / `*_API_URL`** in one place when the port moves.
|
|
1101
1102
|
- **Hardcoded PocketBase (or tool) versions in install scripts.** Pinning `0.25.x` in a script while the ecosystem moved on causes mysterious admin/API failures. Resolve **latest at install** unless `.env` pins a tested semver (§4.2.2).
|
|
1102
1103
|
- **Terminal-only onboarding for non-technical users.** If the README is only shell commands, add **setup.bat** / **setup.sh**, **run.bat** / **run.sh**, **status.bat** / **status.sh** (§4.5).
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Pin Dependabot High transitives with overrides; do not major-upgrade frameworks for nested advisories
|
|
3
|
+
globs:
|
|
4
|
+
- "**/package.json"
|
|
5
|
+
- "**/pnpm-lock.yaml"
|
|
6
|
+
alwaysApply: false
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Lockfile advisory pins
|
|
10
|
+
|
|
11
|
+
## Overrides, not framework jumps
|
|
12
|
+
|
|
13
|
+
- When Dependabot names a **transitive** (`sharp`, `fast-uri`, `cookie`, `js-yaml`) in a lockfile, add `pnpm.overrides` (or npm `overrides`) in the **package that owns that lockfile**.
|
|
14
|
+
- Do **not** major-upgrade Vite 4 / SvelteKit 1 (or another EOL line) just to pick up the nested pin.
|
|
15
|
+
- Recurring pins as of 2026-09: `sharp` `0.35.4`, `fast-uri` `3.1.7`, `cookie@<0.7.0` → `0.7.2`, `js-yaml` `3.15.2`, `brace-expansion` `5.0.9` (or `1.1.18` / `2.1.4` / `3.0.6` on older majors), `launch-editor` `2.14.1`, `qs` `6.16.0`, `hono` `4.13.7`. Cookie 1.x is already past the 0.6 advisory.
|
|
16
|
+
- Vitest 3.x: no patch for `@vitest/mocker` path traversal. Bump the **test** dep to `4.1.11`. That is not a Vite 4 / SvelteKit 1 product upgrade.
|
|
17
|
+
- **Dismiss, do not rewrite:** Vite 4 / SvelteKit 1 / Svelte 3 / devalue 4 / undici 5 have **no patch on that major**. GitHub wants Vite 5.4+ / 6.4+, devalue 5.6+, undici 6.24+, Svelte 4/5, Kit 2. Those alerts stay until a planned framework jump. Mark them “no patch on this major; local/dev-only” after the pin pass.
|
|
18
|
+
|
|
19
|
+
## After the pin
|
|
20
|
+
|
|
21
|
+
- Run `pnpm install --lockfile-only` in that package directory.
|
|
22
|
+
- Leave the product version alone unless this is a real release.
|
|
23
|
+
|
|
24
|
+
## Scanner hygiene (related)
|
|
25
|
+
|
|
26
|
+
- CodeQL `/\/+$/` and `\\0+$` are trailing-delimiter strips: walk the string.
|
|
27
|
+
- Incomplete escaping: `replaceAll` for `/`; escape `\\` before `|` or `"`.
|
|
28
|
+
- Do not file GHSA/CVE for first-wave lockfile or local-stdio findings unless there is a real remote exploit.
|
package/docs/CODE_QUALITY.md
CHANGED
|
@@ -139,11 +139,23 @@ _Run through this list periodically, especially before releases._
|
|
|
139
139
|
- [ ] **Headless browser navigation is guarded too:** a validated `goto` URL does not constrain 3xx, `<meta refresh>`, or script navigation. Abort navigation requests to blocked hosts via request interception and re-check the landed URL before reading content. Note the residual limit: the browser resolves DNS itself unless routed through a controlled proxy.
|
|
140
140
|
- [ ] **Scheme allowlist:** Prefer `protocol === 'http:' || protocol === 'https:'` on a parsed `URL`, not only denying `javascript:`.
|
|
141
141
|
- [ ] **GitHub Actions:** Workflows set top-level `permissions:` (least privilege). Never return `err.stack` / raw exception text in client JSON.
|
|
142
|
+
- [ ] **Dependabot High on lockfiles:** Pin the **named** transitive with `pnpm.overrides` (or npm `overrides`) in the package that owns that lockfile. Do **not** major-upgrade Vite 4 / SvelteKit 1 (or another EOL framework line) just to pick up a nested advisory. Recurring pins as of 2026-09: `sharp` `0.35.4` (libheif), `fast-uri` `3.1.7` (Ajv), `cookie@<0.7.0` → `0.7.2`, `js-yaml` `3.15.2`, `brace-expansion` `5.0.9` (or `1.1.18` / `2.1.4` / `3.0.6` on older majors), `launch-editor` `2.14.1`, `qs` `6.16.0`, `hono` `4.13.7`. Cookie 1.x already sits past the 0.6 advisory; leave it. Vitest 3.x has **no** patch for the `@vitest/mocker` path-traversal advisory: bump the **devDependency** to `4.1.11` (not a product-framework jump). Vite 4 / Kit 1 / Svelte 3 / devalue 4 / undici 5 have **no patch on that major**: dismiss as “no patch on this major; local/dev-only” after the pin pass.
|
|
143
|
+
- [ ] **CodeQL polynomial `/\/+$/` / `\\0+$`:** These are trailing-delimiter strips, not real ReDoS. Rewrite as a `while (endsWith)` / length walk. Same for last-integer or `in <folder>` parses that used `.+` / `.*`.
|
|
144
|
+
- [ ] **CodeQL incomplete escaping:** `replace('/', '%2f')` and `.replace(/\|/g, '\\|')` are incomplete. Use `replaceAll` (or encode each segment), and escape `\\` **before** the metacharacter.
|
|
145
|
+
- [ ] **Scanner wave ≠ breach:** The first org Dependabot/CodeQL run after enabling Advanced Security is a baseline. Triage production High first. Do **not** file GHSA/CVE for lockfile or local-stdio scanner findings unless there is a real remote exploit.
|
|
142
146
|
|
|
143
147
|
> 💡 **Lesson learned:** **Host matching is not string `includes`.** Scrapers and board classifiers that use `hostname.includes('linkedin.com')` match lookalikes and generate dozens of identical CodeQL alerts. One shared helper (`host === domain || host.endsWith('.' + domain)`) plus call-site migration clears the storm and keeps regional subdomains (`uk.linkedin.com`, `boards.greenhouse.io`) working. Keep path checks on `pathname`. Sibling edge services that cannot import the app module should copy the helper with a sync comment.
|
|
144
148
|
>
|
|
145
149
|
> 📝 **Example:** `hostMatchesDomain('www.linkedin.com', 'linkedin.com')` → true; `hostMatchesDomain('evil-linkedin.com', 'linkedin.com')` → false.
|
|
146
150
|
|
|
151
|
+
> 💡 **Lesson learned:** **Dependabot High on a site or tool lockfile is usually a nested pin, not a product rewrite.** FilePress sites pull `sharp`; Ajv pulls `fast-uri`; SvelteKit still ships `cookie@0.6` next to `cookie@1`. Add `pnpm.overrides` in the package that owns the flagged lockfile, run `pnpm install --lockfile-only`, and leave the app version and the site framework where they are. A fleet scan of lockfiles (skip `node_modules`) finds the same four packages on almost every marketing site. A first-scan dump on an old SvelteKit 1 / Vite 4 client looks huge and “might be old”: most of those Highs have no 4.x patch. Pin what still has a same-major fix (`launch-editor`, `qs`, `hono`) and dismiss the rest until a planned Kit 2 rewrite.
|
|
152
|
+
>
|
|
153
|
+
> 📝 **Example:** `"pnpm": { "overrides": { "sharp": "0.35.4", "fast-uri": "3.1.7", "cookie@<0.7.0": "0.7.2", "brace-expansion": "5.0.9" } }`
|
|
154
|
+
|
|
155
|
+
> 💡 **Lesson learned:** **CodeQL “polynomial regex” on `/\/+$/` is a trailing-slash strip.** Walk the string. The same query flags `replace(/\\0+$/)`, `/^-+|-+$/`, and `.+` / `.*` used to peel `cmd /c` or `in <folder>` off operator logs. Those are not remote ReDoS; they are scanner hygiene. Incomplete-escaping alerts are the sibling: escape `\\` first, then `|` or `"`, and replace every `/` when encoding an npm name.
|
|
156
|
+
>
|
|
157
|
+
> 📝 **Example:** `while (path.length > 1 && path.endsWith('/')) path = path.slice(0, -1)` instead of `path.replace(/\/+$/, '')`.
|
|
158
|
+
|
|
147
159
|
> 💡 **Lesson learned:** **A hardening fix is not done until you run the bypass inputs through it.** A codebase can pass a security audit, ship the recommended guard, and still be exploitable because the guard compares strings where it should compare ranges. Real examples from one Exec Foundry sweep: an SSRF check that blocked `127.0.0.1` but not `127.0.0.2` or `[::ffff:127.0.0.1]` or `localhost.`; a DNS-resolution check whose IPv4-mapped branch used `/^:ffff:/` and could never match the resolver's `::ffff:` output; a filter escaper using SQL-style doubled quotes against a parser that expects backslashes; and an "`&` last" entity decoder that still double-decoded because numeric refs ran first. All four had been reviewed and credited as effective. Add an **AREA 0 fix-efficacy pass** to security re-audits, and land a regression test with every guard so the next refactor cannot quietly undo it.
|
|
148
160
|
>
|
|
149
161
|
> 📝 **Example:** `assert.throws(() => assertSafeUrl('http://[::ffff:169.254.169.254]/'))` is worth more than a paragraph in the audit report claiming metadata endpoints are blocked.
|
package/docs/CONTEXT_PROMPT.md
CHANGED
|
@@ -320,6 +320,9 @@ _Things we tried that failed. Claude should NOT repeat these._
|
|
|
320
320
|
> - **Don't use aggressive language for negative outcomes.** Labels like "Rejected" carry strong negative connotations and demoralize users. Use neutral, factual alternatives ("Not Selected") that describe the outcome without implying personal failure. Keep the internal enum key unchanged for backward compatibility — only change the display label.
|
|
321
321
|
> - **Don't expose backend service names in user-facing error messages.** "Is PocketBase running?" or "Supabase connection failed" means nothing to users and leaks implementation details. Catch blocks in auth flows are the most visible — they're the first thing new users see when something goes wrong. Use plain-language alternatives: "Something went wrong. Check your connection and try again."
|
|
322
322
|
> - **Don't use `hostname.includes('brand.com')` (or full-URL `includes`) for security-ish host allow/deny.** Substring matches accept lookalikes and trip CodeQL incomplete-URL-substring alerts en masse. Use suffix-safe host helpers; never return `err.stack` or raw exception text in HTTP JSON to clients.
|
|
323
|
+
> - **Don't major-upgrade Vite 4 / SvelteKit 1 (or another EOL framework line) to clear a nested Dependabot High.** Pin the named package with `pnpm.overrides` in the lockfile that Dependabot cited (`sharp` `0.35.4`, `fast-uri` `3.1.7`, `cookie@<0.7.0` → `0.7.2`, `js-yaml` `3.15.2`, `brace-expansion` `5.0.9` or the patched line for that major, `launch-editor` `2.14.1`, `qs` `6.16.0`, `hono` `4.13.7`). Vite 4 / Kit 1 / Svelte 3 / devalue 4 / undici 5 have no patch on that major: dismiss those leftovers after the pin pass.
|
|
324
|
+
> - **Don't treat `replace('/', '%2f')` or `.replace(/\|/g, '\\|')` as complete encoding.** Replace every slash; escape `\\` before the metacharacter.
|
|
325
|
+
> - **Don't "fix" CodeQL polynomial `/\/+$/` by keeping the regex.** Strip trailing slashes or NULs with a loop. Do not file GHSA/CVE for first-wave lockfile or local-stdio scanner findings unless there is a real remote exploit.
|
|
323
326
|
> - **Don't classify IP ranges by string prefix or exact equality.** `host === '127.0.0.1'` leaves the rest of `127/8`; string prefixes miss IPv4-mapped IPv6 (`::ffff:7f00:1`) and trailing-dot hosts (`localhost.`). Compare numerically after normalizing the host, and cover each blocked class with a test that fails against the old guard.
|
|
324
327
|
> - **Don't publish the app repo to reserve an npm name.** That is a product snapshot, not a stake. Use a throwaway `0.0.0` stub (`content/cursor-rules/npm-name-hold.mdc`).
|
|
325
328
|
> - **Don't parallelize many SDK list/read calls to the same collection through one client instance** without addressing library auto-cancellation (see Critical Patterns → PocketBase JS SDK). Symptom: aborted requests, empty nested arrays on list endpoints, data “missing” after refresh while single-record fetches still work.
|
package/docs/DEPLOYMENT.md
CHANGED
|
@@ -95,6 +95,8 @@ Typical stack: domain at **Namecheap** (or any registrar), **Cloudflare** for DN
|
|
|
95
95
|
|
|
96
96
|
> 💡 **Lesson learned:** **Keep Node.js and package-manager versions aligned across `package.json`, CI, and production.** Declare `engines.node` (and `packageManager` for pnpm) to match what GitHub Actions, Docker, and the host actually run. Updating CI’s Node version without updating `engines` (or vice versa) produces “works on deploy” / “fails locally” drift and subtle dependency resolution differences. Treat **workflow `node-version`**, **`engines`**, and the **runtime image** as one triplet to bump together.
|
|
97
97
|
|
|
98
|
+
> 💡 **Lesson learned:** **The first GitHub Advanced Security scan is a baseline, not an incident.** Dependabot High on a FilePress or Wrangler site lockfile is usually `sharp` (libheif) or Ajv’s `fast-uri`. Override the named package in that lockfile. Leave cookie 1.x alone; only lift `cookie@<0.7.0`. Do not open a GHSA for a lockfile pin.
|
|
99
|
+
|
|
98
100
|
> 💡 **Lesson learned:** **Rolling restarts and reverse proxies:** A single upstream socket that goes down for process restart can surface **502/503** to users for a few seconds. Prefer **multiple** app instances or staggered slots behind the proxy with **health-checked** upstreams; configure brief **retry** or drain behavior where safe. **Why:** Deploy-time blips generate more noise than many logic bugs.
|
|
99
101
|
|
|
100
102
|
> 💡 **Lesson learned:** **`systemd` stop timeout vs deploy wall-clock:** Raising **`TimeoutStopSec`** (or equivalent) so long requests can **drain gracefully** on SIGTERM is often correct for LLM or upload-heavy apps — but **`systemctl stop`** on the old instance waits **up to** that budget before SIGKILL. If the process does not exit promptly on SIGTERM, **each** deploy’s SSH/CI step can grow by a large fraction of that window. **Why:** Blue-green or rolling scripts typically **stop** the superseded unit after traffic moves; a longer cap fixes mid-request kills but can **double** apparent pipeline duration if shutdown is slow. **Mitigations:** implement a **short** graceful shutdown in the app (close listener, bound wait on in-flight work), pick a **middle-ground** timeout, and **split timings** in CI (build vs deploy) so regressions point at compile vs systemd stop.
|
package/package.json
CHANGED