Skip to content

reviewReport-2025051501 #23

@SisyphusZheng

Description

@SisyphusZheng

项目全面审核报告

审计日期:2026-05-16
审计目录:C:\Users\Administrator\WorkBuddy\Claw\lessjs-origin-main
报告语言:中文 + English
范围说明:递归扫描项目自身 TS/JS 前端、构建、SSR/SSG、Web Component、测试与 CI 配置。排除 node_modules/vendor/deliverables/www/dist/dist-test-ssg-render/ 等依赖、生成物和报告产物。当前工作树仅发现未跟踪目录 dist-test-ssg-render/

验证命令 / Verification:

  • deno task fmt:check:通过,184 files checked。
  • deno task lint:通过,183 files checked。
  • deno task typecheck:通过。
  • deno audit:通过,No known vulnerabilities found。
  • deno task test:通过,493 passed / 0 failed。
  • deno task build:通过,生成 298 HTML 页面,客户端 island JS 总量 175 KB。
  • deno task test:e2e:通过,92 passed。

1. 项目概况

  • 项目类型:Deno workspace 下的 Web Standards-first 前端框架 / Deno workspace web framework, focused on Web Components, SSR/SSG, Declarative Shadow DOM, islands, docs site and CLI.
  • 主框架:LessJS 自研框架 + Lit + Vite + Hono + Deno / LessJS internal framework with Lit, Vite, Hono and Deno.
  • 入口文件:
    • Workspace 配置:deno.json
    • 统一插件入口:packages/app/src/index.ts:52
    • 核心运行时入口:packages/core/src/index.ts:20
    • Vite adapter 入口:packages/adapter-vite/src/index.ts:225
    • 官网构建入口:www/vite.config.ts:12
  • 代码规模(估算):183 个 TS/JS 源码文件,约 31,858 行。packages/ 约 122 文件 / 21,575 行;www/ 约 60 文件 / 10,223 行;functions/ 1 文件 / 60 行。
  • 构建工具:Deno task + Vite 8.0.10 + @lessjs/adapter-vite 三阶段构建;测试使用 Deno test 和 Playwright。

English:

  • Type: Deno workspace web framework and docs site.
  • Main stack: LessJS, Lit, Vite, Hono, Deno, Web Components and DSD.
  • Scale: 183 TS/JS source files, about 31.9K lines.
  • Build: Deno tasks, Vite 8.0.10, LessJS adapter-vite, Playwright e2e.

2. 严重问题(必须立即修复)

  • 问题1:package island 元数据可进入生成 JS,但没有做字符串安全生成和严格校验。

    • 位置:packages/adapter-vite/src/entry-generators.ts:26-28packages/adapter-vite/src/route-scanner.ts:295-303
    • 原因:tagNamemodulePath 被直接拼进生成代码:'${i.tagName}': () => import('${i.modulePath}')scanPackageIslands() 只检查字段存在,没有校验自定义元素名、模块路径、控制字符、引号或换行。
    • 风险:如果未来支持第三方 package / 一键 install / registry hub,恶意包可以通过 islands 元数据破坏生成代码,严重时造成构建期或客户端代码注入。
    • 修复方案:用 JSON.stringify() 生成所有 JS 字面量;新增 validatePackageIslandMeta(),校验 tagName 必须符合 Custom Element name 规则,modulePath 必须是合法 bare specifier / relative URL,禁止引号、换行、控制字符和协议型危险输入;为恶意 tagName/modulePath 添加单元测试。
    • English: Package island metadata is interpolated into generated JavaScript without strict validation. This becomes a code-generation injection risk once third-party packages or a registry are supported.
  • 问题2:PWA service worker 会拦截并缓存同源非静态请求,API 响应可能被缓存。

    • 位置:packages/adapter-vite/src/cli/ssg-render.ts:493-519
    • 原因:fetch handler 只排除了跨域请求;/api/ 只是影响 isAsset 判断,仍会进入 networkFirst(),并在 res.ok 时写入 Cache Storage。
    • 风险:启用 PWA 后,同源 GET API、用户相关响应或未来动态接口可能被缓存;离线时可能返回过期或不该缓存的数据。非 GET 请求也会被 service worker 处理,行为不可预测。
    • 修复方案:在 service worker 顶部加入 if (e.request.method !== 'GET') return;;对 /api//rpc/、认证路径直接 return;只缓存 documentscriptstyleimage 等明确允许的资源;为 /api/status、POST 请求、带 Authorization 请求添加 e2e。
    • English: The generated service worker caches same-origin non-asset responses. API routes must be excluded explicitly.
  • 问题3:构建产物测试实际检查了错误目录,并且缺失时静默跳过。

    • 位置:www/__tests__/build-output.test.ts:13www/__tests__/build-output.test.ts:17www/__tests__/build-output.test.ts:30
    • 原因:DIST = join(import.meta.dirname, '..', '..', 'dist') 指向仓库根目录 dist/,但真实构建输出是 www/dist/。目录不存在时测试直接 return,导致“通过”但没有检查任何产物。
    • 风险:no Hono virtual entryclient island JS < 200KB 两个关键产物约束在单测中是 false positive。发布前可能漏掉大 bundle 或 SSR 内部入口泄漏。
    • 修复方案:改为 join(import.meta.dirname ?? '.', '..', 'dist');如果 www/dist 不存在应 throw,不要跳过;在 CI 中保证该测试运行前执行 deno task build
    • English: Build-output tests point to the wrong directory and silently skip when artifacts are missing. This invalidates two artifact-level safety checks.

3. 高危问题(建议尽快修复)

  • 问题1:大量测试把布尔断言写成 assertExists(boolean),会让 false 也通过。

    • 位置:packages/create/__tests__/cli.test.ts:71-101packages/adapter-vite/__tests__/entry-generators.test.ts:5-94packages/adapter-vite/__tests__/build-manifest.test.ts:52-56
    • 原因:assertExists(false) 不会失败,它只检查值不是 null/undefined
    • 风险:脚手架模板版本、生成代码内容、构建 manifest 阈值等测试可能没有真实断言。例:packages/create/__tests__/cli.test.ts:94-101 仍检查旧版本片段,但即使条件为 false 也会通过。
    • 修复方案:把布尔表达式统一替换为 assert(...)assertEquals(condition, true);为全仓添加 lint 脚本扫描 assertExists(.*includes|assertExists(.*>|assertExists(.*===)
    • English: Many tests use assertExists() for boolean expressions, so false conditions pass. Replace with assert() or assertEquals().
  • 问题2:package island 扫描会在构建期执行包入口代码。

    • 位置:packages/adapter-vite/src/route-scanner.ts:274-284
    • 原因:scanPackageIslands() 通过动态 import(pkg) 读取 islands 数组。
    • 风险:一旦支持第三方 registry 或 less add,安装包的任意顶层代码会在构建期执行,扩大供应链攻击面。
    • 修复方案:优先读取静态 manifest,如 custom-elements.jsonlessjs.manifest.json;仅在显式 trustedPackageCode: true 时允许执行包入口;manifest 必须走 JSON schema 验证。
    • English: Build-time package discovery executes package entry code. Registry-style workflows should use static manifests instead.
  • 问题3:api-consumer 的 AbortController 断开后不会重建,组件重连会一直失败。

    • 位置:www/app/islands/api-consumer.ts:14www/app/islands/api-consumer.ts:226-240www/app/islands/api-consumer.ts:251-268
    • 原因:_abortController 在字段初始化时只创建一次,disconnectedCallback() 调用 abort() 后,后续重连仍复用已 aborted signal。
    • 风险:页面切换、局部重挂载或未来 view transition 复用场景下,该 island 的 API 请求会立即失败,并可能把 abort 当作错误展示。
    • 修复方案:每次请求前创建新的 AbortController;在 catch 中忽略 AbortErrorconnectedCallback() 中如果 signal.aborted 则重建 controller。
    • English: The API demo island reuses an aborted controller after disconnect, causing reconnect fetches to fail.
  • 问题4:TOC 对中文标题生成空 id,中文文档锚点可能失效。

    • 位置:www/app/islands/less-toc.ts:99-108www/app/islands/less-toc.ts:163-168
    • 原因:slug 规则 replace(/[^a-z0-9]+/g, '-') 只保留英文数字。中文标题会得到空字符串,第一个标题 id 可能为空,后续变成 -1
    • 风险:中文 guide 页右侧目录链接可能跳转到 # 或错误锚点,影响真实用户阅读。
    • 修复方案:使用 Unicode slug:text.normalize('NFKD').toLowerCase().replace(/[^\p{Letter}\p{Number}]+/gu, '-');若结果为空,回退到 section-${index};加中文标题单测/e2e。
    • English: The TOC slug generator is ASCII-only. Chinese headings can produce empty anchors.
  • 问题5:无 JS 或本地脚本失败时,anti-flash 可能让整页一直隐藏。

    • 位置:www/vite.config.ts:109-110www/public/theme-init.js:21-23
    • 原因:页面注入 <style id="less-anti-flash">html{visibility:hidden}</style>,只依赖 theme-init.js 删除。
    • 风险:脚本被 CSP、浏览器扩展、网络或用户禁用时,页面不可见。LessJS 的定位是 SSR/SSG 和少 JS,这个行为与产品目标冲突。
    • 修复方案:改成只隐藏 body 短时间,加入 <noscript><style>html{visibility:visible}</style></noscript>;或用 CSS 默认主题避免隐藏整页。
    • English: The anti-flash cloak can hide the whole page forever if JavaScript does not run.
  • 问题6:严格 CSP 下,样式加载优化使用 inline onload 会失效。

    • 位置:www/vite.config.ts:39-48packages/adapter-vite/src/index.ts:311-318packages/core/src/html-escape.ts:100-123
    • 原因:当前结构化 stylesheet 允许传入任意 attrs,站点配置使用 onload="this.media='all'"。由于 structured injection 会设置 allowHeadExtrasScripts = true,运行时不会剥离 on* 事件属性。
    • 风险:如果启用 script-src 'self' 且不允许 inline handler,CSS media 切换不会执行;如果未来开放用户配置 attrs,也会形成 XSS 边界。
    • 修复方案:禁止 inject.stylesheets[].attrs 中的 on* 属性;用普通 stylesheet、preload + 安全 JS 模块,或由 framework 生成受控模式;补 CSP e2e。
    • English: Inline onload attributes conflict with strict CSP and should not be accepted through generic attrs.
  • 问题7:Cloudflare/Deno Deploy workflow 指向不存在的 demo/ 目录。

    • 位置:.github/workflows/deploy-api.yml:7-8.github/workflows/deploy-api.yml:20-23
    • 原因:workflow 触发路径和部署命令都使用 demo/,但当前仓库没有该目录,实际 API 在 functions/api/term.ts
    • 风险:手动触发或后续误用会直接失败;团队会误以为 API 部署链路仍有效。
    • 修复方案:删除该 workflow,或改为 Cloudflare Pages / Deno Deploy 当前真实目录;触发路径改为 functions/**,并加一次 dry-run 验证。
    • English: The deployment workflow references a non-existent demo/ folder. It is stale and will fail.

4. 一般问题(优化项)

  • 问题1:PackageIslandMeta 协议过薄,支撑不了 registry hub、自动注册、自动渲染、自动水合。

    • 位置:packages/core/src/types.ts:11-19
    • 原因:目前只有 tagName/modulePath/strategy,缺少 export 名、SSR renderability、hydrate events、slots、parts、states、diagnostics、bundle cost、a11y 信息。
    • 风险:未来 registry 做大后会靠约定和 README 补信息,工具无法验证。
    • 修复方案:新增 LessComponentManifest 或 CEM-compatible 扩展;先做 less validate-manifest,再做 less add
    • English: The package island protocol is too small for registry automation.
  • 问题2:root formatter 明确排除了路由和博客源码,真实页面代码缺少格式化保护。

    • 位置:deno.json:89-90www/app/routes/blog/[slug].ts:1www/app/routes/decisions/[slug].ts:217-218
    • 原因:www/app/routes/www/content/blog/fmt/fmt:check 排除;部分路由还有 deno-fmt-ignore-file,并出现 import 放在文件底部的情况。
    • 风险:页面代码长期漂移,review 成本增加,也容易掩盖模板字符串中的真实语法问题。
    • 修复方案:把无法格式化的少数文件单独 ignore;其余 route 重新纳入 deno fmt --check;底部 import 移到文件顶部。
    • English: Route code is mostly outside formatting checks, which increases maintenance drift.
  • 问题3:pre-commit hook 覆盖范围小于 CI。

    • 位置:.githooks/pre-commit:11-24
    • 原因:hook 只格式化/lint packages/,typecheck 只覆盖 core/rpc/ui/adapter-lit/signals/create,遗漏 adapter-viteappcontenti18nwww
    • 风险:本地提交前通过,但 CI 才发现构建/类型错误。
    • 修复方案:hook 直接调用 deno task fmt:check && deno task lint && deno task typecheck;或至少同步 root typecheck entrypoints。
    • English: The local pre-commit hook does not match CI coverage.
  • 问题4:prism-init.js 在 Prism 不存在时无限重试。

    • 位置:www/public/prism-init.js:9-12
    • 原因:每 50ms setTimeout(init, 50),没有最大次数或停止条件。
    • 风险:CDN 被拦截或脚本加载失败时,会产生永久后台定时器。
    • 修复方案:加入最大重试次数,例如 20 次;失败后停止并打印一次 debug。
    • English: Prism fallback retries forever if Prism is unavailable.
  • 问题5:demo 组件默认请求外部 API。

    • 位置:packages/ui/src/less-hero-ping.ts:107-129
    • 原因:组件连接后自动 fetch,默认 URL 是 https://less-demo-api.sisyphuszheng.deno.net/api
    • 风险:第三方项目引入该组件时可能意外产生外部请求,影响隐私、离线和企业网络环境。
    • 修复方案:默认不请求;只有设置 api-url 时启用;或默认使用相对路径并在 docs 中明确说明。
    • English: A packaged UI component performs an external request by default.
  • 问题6:终端 demo 文案硬编码测试数量,已经与真实结果不一致。

    • 位置:www/app/shared/term-commands.ts:20www/app/shared/term-commands.ts:35
    • 原因:VERSIONtests 475 passing 为手写常量,当前真实测试结果是 493 passed。
    • 风险:公开 demo 信息漂移,降低可信度。
    • 修复方案:从版本文件/构建时常量生成;测试数量改为不写死,或由 CI 生成。
    • English: The terminal demo has stale hard-coded project statistics.

5. 性能优化建议

  • 渲染性能:

    • 当前 DSD 渲染已有 metrics:packages/core/src/types.ts:416-445,这是优势。
    • 建议把 metrics 输出到构建 manifest,并按 route 展示慢组件、DSD size、最大嵌套深度。
    • English: DSD metrics exist; expose them in build reports per route/component.
  • 打包体积:

    • 当前构建结果客户端 island JS 总量 175 KB,低于 200 KB 预算。
    • 但 lazy island 会在 idle 时全部加载:packages/adapter-vite/src/entry-generators.ts:102-113
    • 建议改为 DOM-gated lazy loading:只有 document.querySelector(tag) 命中时才 import;中期做 route-level island manifest。
    • English: Total JS is within budget, but lazy imports still load all known islands after idle.
  • 请求/资源:

    • www/vite.config.ts:55-98 注入多个 CDN Prism 脚本和 GoatCounter;已有 SRI 和 crossorigin,安全性较好。
    • 建议合并 Prism 语言包或按页面需要加载;企业/离线模式提供本地 vendor 选项。
    • packages/ui/src/less-hero-ping.ts:128-129 默认外部请求应改为 opt-in。
    • English: CDN scripts are protected by SRI, but resource count and default external API calls should be reduced.
  • 运行时:

    • www/public/prism-init.js:9-12 无限重试需封顶。
    • www/app/islands/less-search.ts:174-199 搜索 index 加载失败时静默重置,可增加一次性错误状态,避免用户以为搜索无结果。
    • packages/adapter-vite/src/cli/ssg-render.ts:493-519 service worker 应减少拦截范围。
    • English: Cap retry loops, improve search failure state, and narrow service worker interception.

6. 安全风险清单

  • XSS:

    • 已有防护:Markdown 默认 sanitize:packages/content/src/blog/markdown.ts:101-110;终端命令 escape:www/app/shared/term-commands.ts:10-17headExtras 默认剥离 script/on*:packages/core/src/html-escape.ts:100-123
    • 风险点:package island 代码生成未安全转义:packages/adapter-vite/src/entry-generators.ts:26-28renderDSD()sourceInfo 拼成属性时未 escape:packages/core/src/render-dsd.ts:116-119,并输出到 packages/core/src/render-dsd.ts:271packages/core/src/render-dsd.ts:285unsafeHTML(post.html) 依赖上游 sanitize:www/app/routes/blog/[slug].ts:69-90www/app/routes/decisions/[slug].ts:206-218
    • 修复:所有生成 JS/HTML 的动态字段统一用 JSON.stringifyescapeAttr、schema validation;trustedHtml 必须在文档中标为危险开关。
    • English: Sanitization is present, but generated JS, renderDSD sourceInfo and unsafeHTML trust boundaries need tighter contracts.
  • 敏感信息:

    • 未发现硬编码 API key、secret、token、private key。
    • 发现硬编码外部服务:GoatCounter www/vite.config.ts:95-98,demo API packages/ui/src/less-hero-ping.ts:128,CDN 脚本 www/vite.config.ts:55-91
    • 修复:把 analytics/demo API 标成站点级配置,不进入通用模板默认值。
    • English: No secrets found, but external services are hard-coded in demo/site configuration.
  • 依赖漏洞:

    • deno audit 当前无已知漏洞。
    • 依赖集中在 deno.json:39-59,关键版本包括 Lit 3、Hono 4、Vite 8.0.10、Playwright 1.59.1、sanitize-html 2.17.4。
    • 修复:继续把 deno audit 保留在 CI:.github/workflows/test.yml:28-43
    • English: No known vulnerabilities from deno audit; keep audit in CI.
  • 配置风险:

    • PWA SW 缓存边界过宽:packages/adapter-vite/src/cli/ssg-render.ts:493-519
    • packages/create/deno.json:5 使用 deno run -A cli.ts,本地任务权限过宽。
    • e2e 任务使用 -Adeno.json:85,可以接受但应限制在测试环境。
    • English: Main config risks are broad SW caching and broad Deno permissions in local/test tasks.

7. 代码规范与可维护性

  • 命名:

    • 包结构清晰,@lessjs/coreadapter-viteadapter-litcontenti18nuiapp 分层合理。
    • PackageIslandMeta 命名准确,但协议字段不足:packages/core/src/types.ts:11-19
    • English: Package naming is clear; package-island protocol needs expansion.
  • 注释:

    • 优点:关键安全修复和 ADR 背景有注释,例如 packages/core/src/html-escape.ts:100-123
    • 问题:部分注释记录版本历史过多,容易变成 changelog;建议把历史移到 ADR/CHANGELOG,源码只保留当前原因。
    • English: Comments explain safety decisions, but version-history comments should be reduced.
  • 结构:

    • 优点:packages/app/src/index.ts:52-74 通过共享 LessBuildContext 串联 core/content/i18n,结构清晰。
    • 问题:adapter-vite 仍承担 route scan、entry generation、SSG、PWA、CSP、build manifest 等多职责,后续 registry/renderer kernel 应拆出 manifest/validation 子模块。
    • English: The app entry is clean; adapter-vite carries many build responsibilities.
  • 重复代码:

    • 终端命令已抽到共享模块:www/app/shared/term-commands.ts:1-8,这是正向改进。
    • 测试断言模式重复错误:assertExists(boolean) 多处出现,应批量修复。
    • English: Shared command logic is good; repeated assertion misuse should be fixed repo-wide.

8. 构建/依赖风险

  • 依赖冲突:

    • 当前 deno task typecheckdeno task builddeno task test:e2e 均通过,未发现实际依赖冲突。
    • package 内部 imports 多处依赖 jsr:@lessjs/*@^0.14.8,包版本为 0.14.11,语义上可解析到同一 minor,但发布时仍建议统一自动更新。
    • English: No current dependency conflict, but internal JSR version ranges should be kept mechanically synchronized.
  • 版本不兼容:

    • packages/create/__tests__/cli.test.ts:94-101 的旧版本片段说明脚手架测试仍有历史遗留。
    • 修复:测试应从 buildTemplates() 的输入 fake version 断言,而不是写死旧版本。
    • English: Create-package tests still contain stale version expectations.
  • 打包异常点:

    • 真实构建通过;Phase 1 临时 SSR 大 bundle 会在后处理清理,构建日志显示已移除 3 个 SSR artifact。
    • 风险在于 www/__tests__/build-output.test.ts:13-30 当前没有检查真实 www/dist
    • English: Build passes, but artifact tests currently do not validate the actual output directory.
  • 环境配置问题:

    • .github/workflows/deploy-api.yml:20-23 指向不存在的 demo/
    • .githooks/pre-commit:11-24 本地校验范围低于 CI。
    • deno.json:89-90 排除了大量 route 源码格式化。
    • English: Deployment workflow is stale, pre-commit is narrower than CI, and route formatting is excluded.

9. 总体评分(1-10)

  • 代码质量:7.6
  • 安全性:7.1
  • 性能:7.4
  • 可维护性:7.2
  • 综合评分:7.3

English:

  • Code quality: 7.6
  • Security: 7.1
  • Performance: 7.4
  • Maintainability: 7.2
  • Overall: 7.3

评分理由:当前工程验证非常强,fmt/lint/typecheck/audit/test/build/e2e 全部通过;核心架构方向清晰,DSD/island/build 分层有实际代码支撑。扣分主要来自 registry/一键安装方向的供应链边界尚未收紧、service worker 缓存策略过宽、若干测试是假阳性、route 格式化和本地 hook 覆盖不足。

10. 最终修复优先级清单(TOP 10)

  1. 修复 package island 生成代码注入风险:entry-generators.ts:26-28 改用 JSON.stringifyroute-scanner.ts:295-303 增加 schema validation。
  2. 修改 PWA service worker:跳过非 GET、/api/、认证请求,只缓存明确静态资源。
  3. 修复 www/__tests__/build-output.test.ts:13-30 的 dist 路径和静默跳过。
  4. 批量替换 assertExists(boolean)assert() / assertEquals()
  5. 把 package island 扫描从动态 import 改为静态 manifest 优先。
  6. 修复 api-consumer AbortController 重连失败问题。
  7. 修复 less-toc 中文标题 slug 生成,增加中文 TOC 测试。
  8. 给 anti-flash 加 no-JS fallback,避免脚本失败导致空白页。
  9. 删除或修正 .github/workflows/deploy-api.yml 中不存在的 demo/ 部署路径。
  10. 缩小 deno.json formatter 排除范围,并让 .githooks/pre-commit 调用 root 级 fmt:check/lint/typecheck

English TOP 10:

  1. Escape and validate package island metadata before generating JS.
  2. Narrow service worker caching to safe GET static resources.
  3. Fix build-output tests to check www/dist and fail when missing.
  4. Replace boolean assertExists() usages with real boolean assertions.
  5. Prefer static manifests over build-time package imports.
  6. Recreate AbortController in api-consumer after reconnect.
  7. Support Unicode/fallback slugs in less-toc.
  8. Add no-JS fallback for the anti-flash cloak.
  9. Remove or fix the stale demo/ deploy workflow.
  10. Re-align formatting and pre-commit checks with root CI tasks.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions