Skip to content
Report library
Purpose / Development

Lark Apps Skill Security Audit

What the author says it does (original text)

妙搭(Spark/Miaoda)应用开发与托管:应用创建、本地全栈开发、云端生成迭代、创意设计(UI mockup / 可交互原型 / 线框图 / 落地页 / 仪表盘 / 幻灯片 deck / 视觉探索)、AI相关能力和飞书平台能力或者其他外部能力集成、日志/Trace/监控指标/PV/UV 查询、环境变量管理、应用协作者与协作权限设置、应用角色与成员管理、自动化触发器(定时/记录变更/Webhook/飞书审批)。当用户要开发/新建一个系统·工具·平台·应用,或要本地开发 / 云端开发 / 修改 / 部署 / 发布 / 上线 / 拿可分享链接,或用 HTML 做页面·网站·部署到妙搭,或要设计 / design / mockup / prototype / wireframe / 做 PPT / deck / 视觉探索,或提到妙搭/Spark/Miaoda(应用运行时域名形如 *.aifo

Independent security check

Do not install or run it yet

Files checked
54
Risks found
11
Could it run dangerous commands?Looks for programs run straight after downloading, remote control of your computer, and hidden commands.Risks found: 4
High risk

Exported HTML can retain executable attributes and active embedded content

Source references: 4
What we found

The clone routine explicitly removes only comments and SCRIPT elements. Other elements retain their attributes through cloneNode(false), and their children are recursively copied. It does not remove event attributes such as onload/onclick or exclude active elements such as iframe, object, embed, or form before writing a standalone HTML document.

Why this matters

If an artboard contains malicious or contaminated HTML, opening the download could execute JavaScript, load external pages, or submit data. Such code could also read content included in the exported page and send it over the network.

The source supports this risk, but it occurs when the user chooses “Download HTML” and later opens the file. The cloner excludes only comments and `SCRIPT`; `cloneNode(false)` retains attributes on other elements. The result is written into a standalone HTML document without active-content filtering. If an artboard contains untrusted event attributes, iframes, objects, or similar content, the export may preserve their behavior. Users can ask for tag/attribute allowlisting and open exports in an isolated environment.

creative-design/starter-components/design-canvas.jsx:1019In the code
  const cloneStyled = (src) => {    if (src.nodeType === 8 || (src.nodeType === 1 && src.tagName === 'SCRIPT')) return document.createTextNode('');    const dst = src.cloneNode(false);    if (src.nodeType === 1) {      const cs = getComputedStyle(src); let txt = '';      for (let i = 0; i < cs.length; i++) txt += cs[i] + ':' + cs.getPropertyValue(cs[i]) + ';';      dst.setAttribute('style', txt + 'animation:none;transition:none;');      if (src.tagName === 'CANVAS') try { const im = document.createElement('img'); im.src = src.toDataURL(); im.setAttribute('style', txt); return im; } catch {}    }    for (let c = src.firstChild; c; c = c.nextSibling) dst.appendChild(cloneStyled(c));    return dst;  };
Show 3 other places
creative-design/starter-components/design-canvas.jsx:1053In the code
  const xml = new XMLSerializer().serializeToString(clone);  const save = (blob, ext) => {    if (!blob) return;    const a = document.createElement('a');    a.href = URL.createObjectURL(blob); a.download = name + '.' + ext; a.click();    setTimeout(() => URL.revokeObjectURL(a.href), 1000);  };  if (kind === 'html') {    const html = '<!doctype html><html><head><meta charset="utf-8"><title>' + name + '</title>' +      (fontCss ? '<style>' + fontCss + '</style>' : '') +      '</head><body style="margin:0">' + xml + '</body></html>';    return save(new Blob([html], { type: 'text/html' }), 'html');  }
creative-design/starter-components/design-canvas.jsx:1061In the code
  if (kind === 'html') {    const html = '<!doctype html><html><head><meta charset="utf-8"><title>' + name + '</title>' +      (fontCss ? '<style>' + fontCss + '</style>' : '') +      '</head><body style="margin:0">' + xml + '</body></html>';    return save(new Blob([html], { type: 'text/html' }), 'html');  }
creative-design/starter-components/design-canvas.jsx:1206In the code
            </button>            {menuOpen && (              <div className="dc-menu" onPointerDown={(e) => e.stopPropagation()}>                <button onClick={() => doExport('png')}>下载 PNG</button>                <button onClick={() => doExport('html')}>下载 HTML</button>                <button className="dc-danger"
Medium risk

Runs code that is decided at runtime

Source references: 4
What we found

The final command comes from a variable, so this static check cannot confirm exactly what will run.

Why this matters

The hidden content could run extra commands. We cannot yet tell what those commands would do.

Legitimate use of this code

Here, `re.exec` is a regular-expression match used to find `url(...)` references in CSS. It is not JavaScript `eval`, dynamic command execution, or an interpreter call. The match is only used to resolve resource URLs and replace CSS text.

This assessment concerns the code and conditions shown, not proof that harm has occurred.
Legitimate use of this code

This is also only the regular-expression `exec` method enumerating URLs in `background-image`. It does not run the matched content as code or a command. The URL is passed to the resource-conversion function and used to update the exported clone's style.

This assessment concerns the code and conditions shown, not proof that harm has occurred.
creative-design/starter-components/design-canvas.jsx:1011In the code
    let out = rule.css, m; const re = /url\((['"]?)([^'")]+)\1\)/g;    while ((m = re.exec(rule.css))) {      if (m[2].indexOf('data:') === 0) continue;
Show 3 other places
creative-design/starter-components/design-canvas.jsx:1010In the code
  const fontCss = (await Promise.all(fontRules.map(async (rule) => {    let out = rule.css, m; const re = /url\((['"]?)([^'")]+)\1\)/g;    while ((m = re.exec(rule.css))) {      if (m[2].indexOf('data:') === 0) continue;      let abs; try { abs = new URL(m[2], rule.base).href; } catch { continue; }      out = out.split(m[0]).join('url("' + await toDataURL(abs) + '")');    }
creative-design/starter-components/design-canvas.jsx:1045In the code
    let m; const re = /url\(["']?([^"')]+)["']?\)/g;    while ((m = re.exec(bg))) {      const tok = m[0], url = m[1];
creative-design/starter-components/design-canvas.jsx:1042In the code
  });  [clone, ...clone.querySelectorAll('*')].forEach((el) => {    const bg = el.style.backgroundImage; if (!bg) return;    let m; const re = /url\(["']?([^"')]+)["']?\)/g;    while ((m = re.exec(bg))) {      const tok = m[0], url = m[1];      if (url.indexOf('data:') === 0) continue;      jobs.push(toDataURL(url).then((d) => { el.style.backgroundImage = el.style.backgroundImage.split(tok).join('url("' + d + '")'); }));    }  });
Medium risk

Generated pages execute remote CDN JavaScript without integrity verification

Source references: 3
What we found

The starter template loads React, ReactDOM, and in-browser Babel from a Feishu CDN. Although versions are pinned, the script tags have no `integrity` check or local copy, so every page load trusts whatever executable code the CDN returns.

Why this matters

If the CDN, distribution path, or upstream publishing process is compromised, substituted scripts could read page content, observe input, and make network requests with the page's privileges. The browser has no integrity hash with which to detect replacement.

The starter loads three executable scripts remotely. Their versions are pinned, reducing accidental upgrade risk, but the tags have no `integrity` attribute, so every load still trusts the CDN and delivered response. If the CDN, publishing path, or an upstream account is compromised, returned code could read or alter data available to the page. Users can ask for audited self-hosted copies, verifiable SRI where stable CDN content permits it, and a restrictive script CSP.

creative-design/assets/index.html:9In the instructions
  <script>window.gfdatav1={"env":"prod","envName":"prod","runtime":"node","ver":"1.0.0.126","canary":0,"idc":"hl","region":"CN","vdc":"hl","vregion":"China-North","extra":{"canaryType":null}}</script><script    src="https://sf3-scmcdn-cn.feishucdn.com/obj/feishu-static/miaoda/coding-unpkg-sdk/react@18.3.1/umd/react.development.js"    crossorigin="anonymous"></script>  <script    src="https://sf3-scmcdn-cn.feishucdn.com/obj/feishu-static/miaoda/coding-unpkg-sdk/react-dom@18.3.1/umd/react-dom.development.js"    crossorigin="anonymous"></script>  <script    src="https://sf3-scmcdn-cn.feishucdn.com/obj/feishu-static/miaoda/coding-unpkg-sdk/@babel/standalone@7.29.0/babel.min.js"    crossorigin="anonymous"></script>  <!-- 其他内容 -->
Show 2 other places
creative-design/assets/index.html:8In the instructions
  <title></title>  <script>window.gfdatav1={"env":"prod","envName":"prod","runtime":"node","ver":"1.0.0.126","canary":0,"idc":"hl","region":"CN","vdc":"hl","vregion":"China-North","extra":{"canaryType":null}}</script><script    src="https://sf3-scmcdn-cn.feishucdn.com/obj/feishu-static/miaoda/coding-unpkg-sdk/react@18.3.1/umd/react.development.js"    crossorigin="anonymous"></script>  <script    src="https://sf3-scmcdn-cn.feishucdn.com/obj/feishu-static/miaoda/coding-unpkg-sdk/react-dom@18.3.1/umd/react-dom.development.js"    crossorigin="anonymous"></script>  <script    src="https://sf3-scmcdn-cn.feishucdn.com/obj/feishu-static/miaoda/coding-unpkg-sdk/@babel/standalone@7.29.0/babel.min.js"    crossorigin="anonymous"></script>  <!-- 其他内容 -->
creative-design/creative-design.md:136In the instructions
## React + Babel(浏览器内 JSX)当用浏览器内 JSX 编写 React 原型(无构建步骤——Babel 在运行时转译)时,你必须使用下面这些锁定版本的确切 script 标签。不要使用未锁定版本(例如 react@18)。要用 React + Babel 时,可直接从本 skill 的 `assets/index.html` 拷贝 HTML 模板起步(`cp <本 skill 所在目录>/assets/index.html <任务目录>/index.html`)——它已带好这三个 script 标签和 `#root` 挂载点,不必手写。```html<script src="https://sf3-scmcdn-cn.feishucdn.com/obj/feishu-static/miaoda/coding-unpkg-sdk/react@18.3.1/umd/react.development.js" crossorigin="anonymous"></script><script src="https://sf3-scmcdn-cn.feishucdn.com/obj/feishu-static/miaoda/coding-unpkg-sdk/react-dom@18.3.1/umd/react-dom.development.js" crossorigin="anonymous"></script><script src="https://sf3-scmcdn-cn.feishucdn.com/obj/feishu-static/miaoda/coding-unpkg-sdk/@babel/standalone@7.29.0/babel.min.js" crossorigin="anonymous"></script>```
Medium risk

Local development and plugin workflows permit unpinned dependency installation

Source references: 4
What we found

The local workflow runs `npm install`, and plugin installation selects the latest version when no version is supplied. Installation retrieves code from a package source and may run package-declared lifecycle scripts; “latest” can also change after the Skill was reviewed.

Why this matters

A compromised, malicious, or later-changed dependency could execute code on the development machine, read files accessible from the project, or alter build output. The resulting installation is also harder to reproduce and audit.

What this evidence establishes

The plugin command explicitly installs the latest version when `--version` is omitted, creating a time-varying supply-chain risk; users can require an explicit reviewed version. However, whether `npm install` is unpinned depends on the unavailable package manifest and lockfile, and the cited source does not state that lifecycle scripts execute. The candidate's full npm claim is therefore not established. Plugin installation matches the stated integration purpose, but should occur only when the user requests that capability.

This assessment concerns the code and conditions shown, not proof that harm has occurred.
references/lark-apps-local-dev.md:26In the instructions
# 进入仓库后按项目脚手架启动cd ./approval-appnpm installnpm run dev
Show 3 other places
references/lark-apps-plugin-install.md:15In the instructions
- `--name <key>`:插件包 key(从仓库 Skill 的「AI 插件目录」获取)。不传则批量安装 `actionPlugins` 中声明的所有插件。- `--version <ver>`:指定版本(如 `1.0.0`)。不传则安装最新版。
references/lark-apps-plugin-install.md:22In the instructions
```bash# 安装最新版lark-cli apps +plugin-install --name <plugin-key># 安装指定版本lark-cli apps +plugin-install --name <plugin-key> --version 1.0.0# 批量安装已声明的所有插件lark-cli apps +plugin-install```
references/lark-apps-plugin-install.md:9In the instructions
用户要接入 AI 能力或飞书平台能力,需要先安装对应的插件包。安装后才能创建插件实例。具体有哪些可用插件、该选哪个,读取创建的应用仓库 Skill:`.agents/skills/plugin-guide/SKILL.md`。**插件包 ≠ npm 包**:插件包写入 `actionPlugins`,npm 写入 `dependencies`,两套独立机制。禁止用 `npm install` 代替本命令。
Could it expose your files or keys?Looks for uploads of files containing passwords or keys, and keys written directly in the code.Risks found: 3
Medium risk

An ordinary design request may be uploaded and released to Miaoda automatically

Source references: 6
What we found

The creative workflow makes app creation, remote-repository initialization, commit, push, and release default steps. It does not clearly separate local-only output from authorization to upload and publish. A separate legacy publishing guide says deployment should happen only when the user explicitly requests it, creating a conflicting authorization boundary.

Why this matters

Design content, derived attachment content, and source code can leave the local environment, enter the user's Miaoda account and remote Git repository, and become an online release. The link may initially be restricted, but the cloud copy and account asset have still been created and may become more widely visible if access is later broadened.

The skill covers ordinary mockups, prototypes, and decks, while its active workflow requires every new task to create a cloud app/repository, then commit, push, and publish for a link. It does not separately check for upload authorization before publishing, so a user seeking only a local design could have files and content sent to Miaoda. Users can ask that local-only delivery be the default and that app creation, push, and release require explicit publishing intent. The legacy `html-publish` restriction does not govern this new creative-mode flow.

creative-design/creative-design.md:18In the instructions
3. 列出 todo 清单。4. 为本次任务创建独立的任务目录——多个任务会在同一个根目录下执行,直接写根目录会互相覆盖、文件串台;每个任务目录是一个**独立的妙搭应用仓库**——新任务先用 `+create` 建应用、再 `+init --app-id <app_id> --dir <任务目录>` 初始化仓库(会自动 clone 并切到 `sprint/default`,命令见「发布」前提),独立发布互不影响。把资源复制进任务目录,在其中创建交付物。用图片素材提升美观度与丰富度、或需要有依据的内容时,按「图像素材与外部信息」补充。5. (如有)自检React + Babel路径是否正确;ReactDOM.createRoot 是否参数正确,对应元素是否存在6. 收尾:提交你的改动。7. 发布:把产物发布到妙搭拿到可访问链接(见下方「发布」)。写完不发布,用户拿不到线上链接。8. 极其简短地总结——只讲注意事项与后续步骤,并给出发布后的可访问链接。
Show 5 other places
creative-design/creative-design.md:208In the instructions
```bash# 1. 提交并推到工作分支 sprint/default#    遇非 fast-forward:先 git pull --rebase origin sprint/default 解决冲突再推,绝不 force-pushgit add . && git commit -m "feat: ..." && git push origin sprint/default# 2. 发起部署(记下返回的 release_id),然后轮询状态直到 finished / failed:#    publishing → 继续轮询;finished → 输出含可分享的 online_url,直接返回给用户;failed → 按输出中的 error_logs 报告失败原因lark-cli apps +release-create --app-id <app_id> --as userlark-cli apps +release-get --app-id <app_id> --release-id <release_id> --as user```
references/lark-apps-html-publish.md:43In the instructions
## 预览与发布边界- 用户只说“用 HTML 写个 PPT/页面给我看看”时,先生成本地文件或目录,返回路径并问是否发布到妙搭分享;不要默认创建应用或部署。- 用户明确说“部署出去/发链接/可分享”时,才创建 `html` 应用并用 `+html-publish`。- 用户要发布但没有 app_id 时,先 `+create --app-type html` 创建应用;应用名可从页面/站点主题生成,不要让用户手动提供 app_id。- 若产物首页不是 `index.html`,发布前改名或复制为 `index.html`;目录发布时只传干净产物目录,例如 `./dist`。`.git` 目录会被自动排除,不会进入压缩包。
creative-design/creative-design.md:3In the instructions
name: creative-designdescription: 以自包含 HTML 创建精致的设计产物:UI mockup、可交互原型、线框图(wireframe)、落地页、仪表盘、应用屏幕、移动 App、幻灯片 deck(即 PPT / PowerPoint 演示文稿)、动画视频(motion graphics、产品演示 Demo 动画、数据动画)、可视化报告 / 信息图(infographic)/ 视觉长图与视觉探索。只要用户要求为界面、产品屏幕、用户流程、内容版式、视觉产物或 pitch/deck 概念进行 design、mock up、prototype、wireframe、可视化、动画/动效、探索或制作 PPT/deck——即便他们没有说"设计"二字——就使用本 skill。Harness 无关:适用于 Aily、Claude Code、Codex Agent 及类似的具备文件能力的 agent。---
creative-design/creative-design.md:187In the instructions
## 发布设计产物写完并提交后,需要发布到妙搭(lark-apps)才能拿到可访问链接。本 skill 产出的是创意模式(html)应用,发布走本地开发链路:改动 git commit 后推到工作分支 `sprint/default`,再用 `lark-cli apps` 命令发起部署并轮询结果。
SKILL.md:36In the instructions
| HTML 应用 / 创意模式 — 写 HTML 页面/网站、静态页、PPT/deck、落地页、仪表盘、UI mockup、原型、线框图、视觉探索 | 加载 [`creative-design/creative-design.md`](creative-design/creative-design.md)(含完整开发与发布流程) | [`creative-design/creative-design.md`](creative-design/creative-design.md) || 旧版存量 HTML 应用(无 Git 管理)继续上传已有静态产物 | `+html-publish`(仅兼容旧链路;新建 html / 创意模式 / creative-design 产物不得使用) | [`lark-apps-html-publish.md`](references/lark-apps-html-publish.md) || 开发已有应用 / 初始化本地仓库(开发方式已定为本地后;先解析 app_id,勿 `+create` 新建) | `+init`(或手动 `+git-credential-init` + 原生 git)。**执行前必读** [`lark-apps-local-dev.md`](references/lark-apps-local-dev.md),含端到端流程和领域规则 | [`lark-apps-init.md`](references/lark-apps-init.md), [`lark-apps-git-credential.md`](references/lark-apps-git-credential.md) |
Medium risk

The deck component sends complete HTML and speaker notes to a parent page of any origin

Source references: 4
What we found

The component sends its complete `outerHTML` and notes through `window.parent.postMessage` with the wildcard target origin `'*'`, rather than restricting the recipient to a trusted Miaoda origin. Design Canvas uses the same wildcard approach for file-write messages.

Why this matters

If a published page is embedded by a malicious or untrusted site, the parent can receive presentation content and notes when the user deletes, moves, hides, or edits material. Internal plans, customer information, or unpublished data in the deck could be disclosed to the embedding site.

When notes are edited or slides are reordered/deleted, the component sends the notes array or full `outerHTML` to its parent using `postMessage(..., '*')`; Design Canvas likewise sends a file path and content to a wildcard target. If a non-Miaoda site can embed the page, that parent may receive design content and notes. The supplied source does not establish a deployment-level anti-framing policy. Users can ask for an explicit trusted origin, a verified host handshake, and CSP `frame-ancestors` restrictions.

creative-design/starter-components/deck-stage.js:1297In the code
    /** Persist a pure text edit: mirror the in-DOM #speaker-notes tag (if any)     *  for in-iframe consumers, and post the full array to the parent so the     *  host writes it to disk — the tag lives OUTSIDE <deck-stage>, so the     *  deck-changed outerHTML path can't carry notes. */    _emitNotesChange() {      const notes = this._notesForEmit();      this._notes = notes;      const tag = document.getElementById('speaker-notes');      if (tag) tag.textContent = JSON.stringify(notes);      this.dispatchEvent(new CustomEvent('noteschange', {        detail: { notes, index: this._index }, bubbles: true, composed: true,      }));      try { window.parent.postMessage({ type: 'miaoda:deck:notes-changed', index: this._index, notes }, '*'); } catch (e) {}    }
Show 3 other places
creative-design/starter-components/deck-stage.js:2393In the code
      }));      // Forward to parent so the host can persist mutations (the CustomEvent      // cannot cross iframe boundaries). detail.slide is an Element and can't      // survive structured-clone, so only the scalars cross.      try {        window.parent.postMessage({          type: 'miaoda:deck:deck-changed',          action: detail.action, from: detail.from, to: detail.to,          html: this.outerHTML,          notes: this._notesForEmit(),        }, '*');      } catch (e) {}    }
creative-design/starter-components/design-canvas.jsx:225In the code
// Persist a sidecar file back to the host.function miaodaWriteFile(path, content) {  try {    window.parent.postMessage({ type: 'miaoda:bridge:write-file', path: path, content: content }, '*');  } catch (e) {    /* no parent — ignore */  }
creative-design/starter-components/design-canvas.jsx:223In the code
// ─────────────────────────────────────────────────────────────const DC_STATE_FILE = '.design-canvas.state.json';// Persist a sidecar file back to the host.function miaodaWriteFile(path, content) {  try {    window.parent.postMessage({ type: 'miaoda:bridge:write-file', path: path, content: content }, '*');  } catch (e) {    /* no parent — ignore */  }  return Promise.resolve();}
Medium risk

Export automatically requests URLs referenced by the artboard and stylesheets and packages readable responses

Source references: 4
What we found

The exporter fetches stylesheets and recursive @import targets, fonts, images, and background URLs. There is no origin allowlist and no explicit credentials: 'omit'. Readable responses are converted to data URIs, while failed conversions leave the original URL in place.

Why this matters

A malicious or accidental resource URL can trigger network access during export. Readable responses—including same-origin resources the browser permits—may be packaged into the downloaded file. Remote URLs that remain can contact external servers again when an exported HTML file is later opened.

Export is user-triggered, and fetching resources is consistent with producing a self-contained export. However, the implementation does request URLs supplied by the artboard or stylesheets and recursively follows `@import`, with no visible protocol or host allowlist. Untrusted design content could therefore make the browser contact chosen addresses; same-origin requests may carry credentials under browser defaults, and readable responses are embedded in the export. Users can ask for protocol/host restrictions, explicit credential omission, and a pre-export resource list.

creative-design/starter-components/design-canvas.jsx:977In the code
  try { await document.fonts.ready; } catch {}  const toDataURL = (url) => fetch(url).then((r) => r.blob()).then((b) => new Promise((res) => {    const fr = new FileReader(); fr.onload = () => res(fr.result); fr.onerror = () => res(url); fr.readAsDataURL(b);  })).catch(() => url);
Show 3 other places
creative-design/starter-components/design-canvas.jsx:987In the code
  const fontRules = [], pending = [], seen = new Set();  const scrapeCss = (href) => {    if (seen.has(href)) return; seen.add(href);    pending.push(fetch(href).then((r) => r.text()).then((css) => {      for (const m of css.match(/@font-face\s*{[^}]*}/g) || []) fontRules.push({ css: m, base: href });      for (const m of css.matchAll(/@import\s+(?:url\()?['"]?([^'")\s;]+)/g))        scrapeCss(new URL(m[1], href).href);    }).catch(() => {}));  };  const walk = (rules, base) => {    for (const r of rules) {      if (r.type === CSSRule.FONT_FACE_RULE) fontRules.push({ css: r.cssText, base });      else if (r.type === CSSRule.IMPORT_RULE && r.styleSheet) {        const ibase = r.styleSheet.href || base;        try { walk(r.styleSheet.cssRules, ibase); } catch { scrapeCss(ibase); }      } else if (r.cssRules) walk(r.cssRules, base);    }  };  for (const ss of document.styleSheets) {    const base = ss.href || location.href;    try { walk(ss.cssRules, base); } catch { if (ss.href) scrapeCss(ss.href); }  }  while (pending.length) await pending.shift();  const fontCss = (await Promise.all(fontRules.map(async (rule) => {
creative-design/starter-components/design-canvas.jsx:1037In the code
  const jobs = [];  clone.querySelectorAll('img').forEach((el) => {    const s = el.getAttribute('src');    if (s && s.indexOf('data:') !== 0) jobs.push(toDataURL(el.src).then((d) => el.setAttribute('src', d)));  });  [clone, ...clone.querySelectorAll('*')].forEach((el) => {    const bg = el.style.backgroundImage; if (!bg) return;    let m; const re = /url\(["']?([^"')]+)["']?\)/g;    while ((m = re.exec(bg))) {      const tok = m[0], url = m[1];      if (url.indexOf('data:') === 0) continue;      jobs.push(toDataURL(url).then((d) => { el.style.backgroundImage = el.style.backgroundImage.split(tok).join('url("' + d + '")'); }));    }  });  await Promise.all(jobs);
creative-design/starter-components/design-canvas.jsx:1111In the code
  const doExport = (kind) => {    setMenuOpen(false);    if (!cardRef.current) return;    // Unicode-aware sanitize: keep letters (incl. CJK/accented), digits,    // whitespace, dot/underscore/hyphen; collapse everything else to '_'.    // ASCII-only \w would strip Chinese labels down to a bare '_'.    const name = String(label || id || 'artboard').replace(/[^\p{L}\p{N}\s._-]+/gu, '_');    const ew = cardRef.current.offsetWidth || width;    const eh = cardRef.current.offsetHeight || height;    dcExport(cardRef.current, ew, eh, name, kind)      .catch((e) => console.error('[design-canvas] export failed:', e));  };
Could it delete files or keep running?Looks for broad file deletion, disk overwrites, and programs set to start automatically.No risks found
Could it bypass safety checks?Looks for skipped website security checks, excessive file access, or actions that skip your approval.Risks found: 3
High risk

The default table template lets every signed-in user modify all data and anonymous users read all data

Source references: 4
What we found

The SQL template creates a `FOR ALL TO authenticated USING (true)` policy and a `SELECT TO authenticated, anon USING (true)` policy. These rules do not isolate data by user or business role. The adjacent policy named “modify own data” cannot narrow the broader allow-all policy.

Why this matters

A business table created from this template may permit any signed-in app user to read and modify every row, while unauthenticated visitors may read every row. Orders, approvals, personnel records, or other business data could be viewed or altered without the intended authorization.

The recommended table template enables RLS but then grants every `authenticated` identity all operations and permits both `authenticated` and `anon` to read every row. Permissive PostgreSQL policies combine with OR, so the later “modify own data” policy does not narrow those broad grants. For private or per-user data, this could enable unauthorized reading or modification. Users can ask for deny-by-default owner/role policies and tests using anonymous and ordinary authenticated accounts before deployment.

references/lark-apps-db-execute.md:67In the instructions
新建业务表必须:4 个审计列 + 启用 RLS + 4 条默认 policy,**放在同一次 `+db-execute` 调用里**(RLS / policy / COMMENT / INDEX 一起)。裸表名,不写 `public.` 或 schema 前缀。
Show 3 other places
references/lark-apps-db-execute.md:95In the instructions
CREATE POLICY "修改全部数据" ON <table>  AS PERMISSIVE FOR ALL TO authenticated USING (true);CREATE POLICY "查看全部数据" ON <table>  AS PERMISSIVE FOR SELECT TO authenticated, anon USING (true);CREATE POLICY "修改本人数据" ON <table>  AS PERMISSIVE FOR ALL TO authenticated USING (    (current_setting('app.user_id'::text) = ANY (ARRAY[]::text[]))    AND (current_setting('app.user_id'::text) = ((_created_by).user_id)::text)  );```
references/lark-apps-db-execute.md:90In the instructions
ALTER TABLE <table> ENABLE ROW LEVEL SECURITY;CREATE POLICY service_role_bypass_policy ON <table>  TO service_role USING (true);CREATE POLICY "修改全部数据" ON <table>  AS PERMISSIVE FOR ALL TO authenticated USING (true);CREATE POLICY "查看全部数据" ON <table>  AS PERMISSIVE FOR SELECT TO authenticated, anon USING (true);
references/lark-apps-db-execute.md:101In the instructions
CREATE POLICY "修改本人数据" ON <table>  AS PERMISSIVE FOR ALL TO authenticated USING (    (current_setting('app.user_id'::text) = ANY (ARRAY[]::text[]))    AND (current_setting('app.user_id'::text) = ((_created_by).user_id)::text)  );```
Medium risk

The deck accepts control messages from windows of any origin

Source references: 3
What we found

The component registers a global `message` listener, but `_onMessage` does not verify `e.origin` or `e.source` before accepting presentation mode, preview mode, thumbnail-rail state, and navigation commands.

Why this matters

An untrusted page embedding the deck can change presentation state, hide editing UI, or navigate slides, interfering with the user's understanding of the current content and state. Combined with wildcard outbound messages, the parent can also induce interaction under a UI state it controls.

The component listens for every window `message`, while the handler checks only message fields and not `e.origin` or `e.source`. If a third party can embed the page or hold a window reference, it can switch presentation/preview state, change rail visibility, or navigate slides. The visible impact is UI and decision manipulation; these lines do not directly show account or file permissions changing. Users can ask that messages be accepted only from a verified Miaoda origin and the expected parent window.

creative-design/starter-components/deck-stage.js:715In the code
      this._syncPrintPageRule();      window.addEventListener('keydown', this._onKey);      window.addEventListener('resize', this._onResize);      window.addEventListener('message', this._onMessage);      window.addEventListener('hashchange', this._onHashChange);      window.addEventListener('click', this._onDocClick, true);      this.addEventListener('click', this._onTap);
Show 2 other places
creative-design/starter-components/deck-stage.js:1694In the code
    _onMessage(e) {      const d = e.data;      if (d && d.type === 'miaoda:deck:presenting' && typeof d.on === 'boolean') {        this._presenting = d.on;        this._syncMobileMode();        if (!d.on) this._toggleMobUi(false);        this._syncRailHidden();        this._syncNotesHidden();        this._closeMenu();        this._fit();        this._scaleThumbs();      }      // Host's Preview segment (ViewerMode='none'): the rail's drag-reorder /
creative-design/starter-components/deck-stage.js:1724In the code
      // doesn't change rail visibility. Persists alongside rail width.      if (d && d.type === 'miaoda:deck:rail-visible' && typeof d.on === 'boolean') {        if (d.on === this._railVisible) return;        this._railVisible = d.on;        try { localStorage.setItem('deck-stage.railVisible', d.on ? '1' : '0'); } catch (e) {}        // Notify the parent so its toolbar state stays in sync.        try { window.parent.postMessage({ type: 'miaoda:deck:rail-visible-changed', on: d.on }, '*'); } catch (e) {}        // Arm the transition, commit it, then flip state — otherwise the        // browser coalesces both writes and nothing animates on show.        this.setAttribute('data-rail-anim', '');        void (this._rail && this._rail.offsetHeight);        this._syncRailHidden();        this._fit();        this._scaleThumbs();        clearTimeout(this._railAnimTimer);        this._railAnimTimer = setTimeout(() => this.removeAttribute('data-rail-anim'), 220);      }      if (d && d.type === 'miaoda:deck:rail-enabled') this._enableRail();      if (d && d.type === 'miaoda:deck:goto' && typeof d.index === 'number') this._go(d.index | 0, 'api');    }
Medium risk

Canvas and tweak controls accept cross-window commands without validating the sender

Source references: 4
What we found

Both message listeners inspect only the data type and do not validate e.origin or e.source. Any page holding a reference to the window can send zoom, probe, tweak-activation, or tweak-deactivation messages.

Why this matters

If the app is embedded by an unexpected page or its window reference is exposed, another page can alter canvas zoom, repeatedly trigger host communication, or show and hide the tweak panel. This can disrupt work and cause the user to review a design in a misleading visual state; the shown inbound messages do not directly set tweak values.

Both active `message` listeners check only message type/value and do not validate `e.origin` or `e.source`. Another window that obtains a reference could therefore change zoom or show/hide the tweaks panel; a canvas `probe` also causes status messages to be posted to the parent. The visible impact here is primarily UI control and state messaging, not credential theft. Users can ask that messages be accepted only from the expected parent and an explicit origin.

creative-design/starter-components/design-canvas.jsx:807In the code
    // Host-driven zoom (toolbar % menu). Zooms around viewport centre so the    // visible midpoint stays fixed — matching the host's iframe-zoom feel.    const onHostMsg = (e) => {      const d = e.data;      if (d && d.type === 'miaoda:canvas:set-zoom' && typeof d.scale === 'number') {        const r = vp.getBoundingClientRect();        zoomAt(r.left + r.width / 2, r.top + r.height / 2, d.scale / tf.current.scale);      } else if (d && d.type === 'miaoda:canvas:probe') {        // Host's [readyGen] reset asks whether a canvas is present; it        // fires on the iframe's native 'load', which for canvases with        // images/fonts is after our mount-time announce, so re-announce.        // Clear the pan-tick guard so apply() re-posts the current scale        // even if it's unchanged — the host just reset dcScale to 1.        window.parent.postMessage({ type: 'miaoda:canvas:present' }, '*');        lastPostedScale.current = undefined;        apply();      }    };    window.addEventListener('message', onHostMsg);    // Announce canvas mode so the host toolbar proxies its % control here
Show 3 other places
creative-design/starter-components/tweaks-panel.jsx:234In the code
  React.useEffect(() => {    const onMsg = (e) => {      const t = e?.data?.type;      if (t === 'miaoda:tweaks:activate') setOpen(true);      else if (t === 'miaoda:tweaks:deactivate') setOpen(false);    };    window.addEventListener('message', onMsg);    window.parent.postMessage({ type: 'miaoda:tweaks:available' }, '*');    return () => window.removeEventListener('message', onMsg);  }, []);
creative-design/starter-components/design-canvas.jsx:809In the code
    // visible midpoint stays fixed — matching the host's iframe-zoom feel.    const onHostMsg = (e) => {      const d = e.data;      if (d && d.type === 'miaoda:canvas:set-zoom' && typeof d.scale === 'number') {        const r = vp.getBoundingClientRect();        zoomAt(r.left + r.width / 2, r.top + r.height / 2, d.scale / tf.current.scale);      } else if (d && d.type === 'miaoda:canvas:probe') {        // Host's [readyGen] reset asks whether a canvas is present; it        // fires on the iframe's native 'load', which for canvases with        // images/fonts is after our mount-time announce, so re-announce.        // Clear the pan-tick guard so apply() re-posts the current scale        // even if it's unchanged — the host just reset dcScale to 1.        window.parent.postMessage({ type: 'miaoda:canvas:present' }, '*');        lastPostedScale.current = undefined;        apply();      }    };    window.addEventListener('message', onHostMsg);    // Announce canvas mode so the host toolbar proxies its % control here
creative-design/starter-components/design-canvas.jsx:833In the code
    // scale (before miaoda:canvas:present), so clear the guard to re-post it in order.    window.parent.postMessage({ type: 'miaoda:canvas:present' }, '*');    lastPostedScale.current = undefined;    apply();
Could it mislead the AI or hide text?Checks the skill instructions for requests to ignore you, influence the report, or hide text in invisible characters.Risks found: 1
High risk

Requires a helper to run automatically and out of sight

Source references: 1
What we found

The skill combines automatic execution with instructions not to ask or tell the user.

Why this matters

If the AI follows this text, it may stop following your instructions or skip actions that normally need your approval.

Legitimate use of this code

This line does not require concealment or automatic execution. It explicitly forbids silently adding `--yes` and requires previewing impact, explaining irreversible risk, and obtaining confirmation. The shown `--yes` is for execution after confirmation, not a concealed extra action.

This assessment concerns the code and conditions shown, not proof that harm has occurred.
references/lark-apps-db.md:282In the instructions
- 看表用 `+db-table-list`,看结构用 `+db-table-get`(要建表语句加 `--format pretty`);`+db-env-create` 仅用于存量单库拆多环境,新建的 full_stack 应用一般不需要。- 高危命令(`+db-env-create`、`+db-data-import`、`+db-env-migrate`、`+db-recovery-apply`、`+db-sync-create`、`+db-sync-update`、`+db-sync-delete`)动手前先看清影响再带 `--yes`:发布 / 恢复先跑对应预览 `+db-env-diff` / `+db-recovery-diff`,Base 同步先跑 `+db-sync-create --preview`,导入无预览命令、可先 `--dry-run` 看请求或先在 `--environment dev` 验;不要静默追加 `--yes`,遇 confirmation_required(exit 10)按 lark-shared 协议向用户确认不可逆风险后再补 `--yes` 重试。- 导入 / 导出的本地路径用工作目录内相对路径;超大表导出会被行数 / 体积上限拒,改用 `+db-execute` 分批。
Could it change links or payment recipients without asking?Looks for forced referral or payment changes combined with instructions to hide the change.No risks found

Inside this skill

8 instruction sections

This Skill manages Miaoda assets as the user, including apps, source repositories, databases, files, keys, members, permissions, and automations. It is not limited to generating local design files.

View source
SKILL.md:13In the instructions
妙搭应用属于用户资产。默认用 `--as user`;认证、scope、exit-10、高风险确认、`_notice` 等通用处理只读 [`../lark-shared/SKILL.md`](../lark-shared/SKILL.md),不要在本 skill 里复制。妙搭应用有两条开发路径:**本地开发**(拉源码本地写)/ **云端会话**(妙搭 AI 生成)。
SKILL.md:39In the instructions
| 本地开发时 `.env.local` 损坏/丢失,重新拉取启动期环境变量 | `+env-pull` | [`lark-apps-env-pull.md`](references/lark-apps-env-pull.md) || 管理应用环境变量(查看/设置/删除) | `+env-list`, `+env-set`, `+env-delete` | [`lark-apps-env.md`](references/lark-apps-env.md) || 查线上日志、Trace、请求数、错误率、延迟、CPU、memory、PV/UV/访问量 | `+log-list`, `+log-get`, `+trace-list`, `+trace-get`, `+metric-list`, `+analytics-list` | [`lark-apps-observability.md`](references/lark-apps-observability.md) || 看表 / 看结构 / 初始化多环境 / 导入导出数据 / 变更追溯 / 行级审计 / dev→online 发布 / 时间点恢复 / 查 DB 用量 | `+db-table-list`、`+db-table-get`、`+db-env-create`、`+db-data-export`/`+db-data-import`、`+db-changelog-list`、`+db-audit-status`/`+db-audit-enable`/`+db-audit-disable`/`+db-audit-list`、`+db-env-diff`/`+db-env-migrate`、`+db-recovery-diff`/`+db-recovery-apply`、`+db-quota-get` | [`lark-apps-db.md`](references/lark-apps-db.md) || 逐条执行 SQL(SELECT / DML / DDL);建表 / 改表 / 写 SQL 的平台规范 | `+db-execute` | [`lark-apps-db-execute.md`](references/lark-apps-db-execute.md)(含「平台 SQL 规范」:审计列 / RLS / `user_profile` / 禁用 SQL / PG 陷阱) || 管理应用文件存储:上传/下载本地文件、列出/查看/删除已存文件、生成临时分享链接、查存储用量 | `+file-upload`/`+file-download`/`+file-list`/`+file-get`/`+file-sign`/`+file-delete`/`+file-quota-get` | [`lark-apps-file.md`](references/lark-apps-file.md) || 调试应用运行时缓存:查看/删除单个业务 key、清空指定环境缓存 | `+cache-get`/`+cache-delete`/`+cache-clear` | [`lark-apps-cache.md`](references/lark-apps-cache.md) |

The creative-design workflow creates a separate Miaoda app repository, commits and pushes it, and starts an online release rather than only generating HTML.

View source
creative-design/creative-design.md:18In the instructions
3. 列出 todo 清单。4. 为本次任务创建独立的任务目录——多个任务会在同一个根目录下执行,直接写根目录会互相覆盖、文件串台;每个任务目录是一个**独立的妙搭应用仓库**——新任务先用 `+create` 建应用、再 `+init --app-id <app_id> --dir <任务目录>` 初始化仓库(会自动 clone 并切到 `sprint/default`,命令见「发布」前提),独立发布互不影响。把资源复制进任务目录,在其中创建交付物。用图片素材提升美观度与丰富度、或需要有依据的内容时,按「图像素材与外部信息」补充。5. (如有)自检React + Babel路径是否正确;ReactDOM.createRoot 是否参数正确,对应元素是否存在6. 收尾:提交你的改动。7. 发布:把产物发布到妙搭拿到可访问链接(见下方「发布」)。写完不发布,用户拿不到线上链接。8. 极其简短地总结——只讲注意事项与后续步骤,并给出发布后的可访问链接。
creative-design/creative-design.md:208In the instructions
```bash# 1. 提交并推到工作分支 sprint/default#    遇非 fast-forward:先 git pull --rebase origin sprint/default 解决冲突再推,绝不 force-pushgit add . && git commit -m "feat: ..." && git push origin sprint/default# 2. 发起部署(记下返回的 release_id),然后轮询状态直到 finished / failed:#    publishing → 继续轮询;finished → 输出含可分享的 online_url,直接返回给用户;failed → 按输出中的 error_logs 报告失败原因lark-cli apps +release-create --app-id <app_id> --as userlark-cli apps +release-get --app-id <app_id> --release-id <release_id> --as user```

Several destructive actions have preview or confirmation gates, including environment-variable deletion, whole-environment cache clearing, database recovery, and role deletion.

View source
SKILL.md:157In the instructions
- **预授权判定**:判断用户是否表达了"放手做完、不用中途逐步问我"的意图——明确免确认(如"别问 / 直接做 / 自己定"),或要求一气呵成做到完成(如"做完部署上线给我")。是 → 整个流程按合理默认往下走、不再逐步确认(含 clone 到派生目录、发布等);否 → 缺失参数(如目录)该问就问、高影响动作先确认。- **禁止预授权判定底线**(即便已预授权也不豁免):① 会删/丢数据或不可逆的 DB 操作(判据见 [`lark-apps-db-execute.md`](references/lark-apps-db-execute.md))先 `--dry-run` 确认;② `+role-delete`、`+role-member-remove --all`、批量移除成员必须先确认 app、role、成员范围和后果,不能从泛化"直接做"推导出 `--yes`;命令式"删除/移除某对象"只确定操作目标,不等于用户已确认不可逆后果,未明确确认时应在说明影响后停下请求确认;③ `+html-publish` 体积超限时(判据见 [`lark-apps-html-publish.md`](references/lark-apps-html-publish.md)),立即停止并转述超限项;④ `+cache-clear` 会清空整个环境的缓存,「用户让我清缓存」只确定了操作目标、不等于确认了这次清空——未拿到对「清空该环境」的明确确认表述时,只出 `--dry-run` 预览或停下请求确认,不得首次调用即自带 `--yes`(判据表见 [`lark-apps-cache.md`](references/lark-apps-cache.md))。
references/lark-apps-db.md:247In the instructions
**`+db-recovery-diff`**:预览把库恢复到 `--target` 时间点会带来哪些变更(受影响的表、行数、预计耗时),不落地。同样需 `spark:app:write` scope。**`+db-recovery-apply`(高危)**:把库恢复到某个时间点,**会覆盖当前数据**,不可逆,必须带 `--yes`。- 可恢复窗口最长 **7 天**,且不早于**最近一次 `+db-env-migrate`**;超出窗口的目标会被拒。- 目标时间点与当前库一致时返回 `no_changes`(空操作),不算失败。- 动手前务必先 `+db-recovery-diff` 给用户确认。

Bundled design components communicate with their parent page and can send design state, speaker notes, or the complete deck HTML to the host.

View source
creative-design/starter-components/design-canvas.jsx:223In the code
// ─────────────────────────────────────────────────────────────const DC_STATE_FILE = '.design-canvas.state.json';// Persist a sidecar file back to the host.function miaodaWriteFile(path, content) {  try {    window.parent.postMessage({ type: 'miaoda:bridge:write-file', path: path, content: content }, '*');  } catch (e) {    /* no parent — ignore */  }  return Promise.resolve();}
creative-design/starter-components/deck-stage.js:2393In the code
      }));      // Forward to parent so the host can persist mutations (the CustomEvent      // cannot cross iframe boundaries). detail.slide is an Element and can't      // survive structured-clone, so only the scalars cross.      try {        window.parent.postMessage({          type: 'miaoda:deck:deck-changed',          action: detail.action, from: detail.from, to: detail.to,          html: this.outerHTML,          notes: this._notesForEmit(),        }, '*');      } catch (e) {}    }

The Skill manages Miaoda application assets as the user and initiates an apps-domain login only when the CLI explicitly reports no login or a missing scope.

View source
SKILL.md:13In the instructions
妙搭应用属于用户资产。默认用 `--as user`;认证、scope、exit-10、高风险确认、`_notice` 等通用处理只读 [`../lark-shared/SKILL.md`](../lark-shared/SKILL.md),不要在本 skill 里复制。妙搭应用有两条开发路径:**本地开发**(拉源码本地写)/ **云端会话**(妙搭 AI 生成)。
SKILL.md:17In the instructions
妙搭应用是用户的个人资产,统一 `--as user`(见开头)。已有用户身份可用时直接执行业务命令,**不要为了预防权限问题主动重新登录**,否则可能中断原任务并触发不必要的设备授权。仅当 CLI 明确返回未登录或缺少本域 scope 时,一次性执行:```bashlark-cli auth login --domain apps```

Canvas export clones the selected artboard DOM and computed styles, inlines fonts and images, and generates a PNG or standalone HTML file when the user selects a download.

View source
creative-design/starter-components/design-canvas.jsx:969In the code
// Per-artboard export (kind: 'png' | 'html'). Both paths share the same// self-contained clone: computed styles baked in, @font-face / <img> /// inline-style background-image urls inlined as data URIs. PNG wraps the// clone in foreignObject→canvas at 3× the artboard's natural width×height// (same pipeline the host uses for page captures); HTML wraps it in a// minimal standalone document. Both are independent of viewport zoom.async function dcExport(node, w, h, name, kind) {  try { await document.fonts.ready; } catch {}
creative-design/starter-components/design-canvas.jsx:1206In the code
            </button>            {menuOpen && (              <div className="dc-menu" onPointerDown={(e) => e.stopPropagation()}>                <button onClick={() => doExport('png')}>下载 PNG</button>                <button onClick={() => doExport('html')}>下载 HTML</button>                <button className="dc-danger"                  onClick={() => { if (confirming) { setMenuOpen(false); onDelete(); } else setConfirming(true); }}>                  {confirming ? '确认删除' : '删除'}                </button>              </div>

Tweak-panel values are not preview-only: every update is sent to the parent, and the comment states that the host rewrites the on-disk EDITMODE block.

View source
creative-design/starter-components/tweaks-panel.jsx:175In the code
// ── useTweaks ───────────────────────────────────────────────────────────────// Single source of truth for tweak values. setTweak persists via the host// (miaoda:tweaks:set-keys → host rewrites the EDITMODE block on disk).function useTweaks(defaults) {  const [values, setValues] = React.useState(defaults);  // Accepts either setTweak('key', value) or setTweak({ key: value, ... }) so a  // useState-style call doesn't write a "[object Object]" key into the persisted  // JSON block.  const setTweak = React.useCallback((keyOrEdits, val) => {    const edits = typeof keyOrEdits === 'object' && keyOrEdits !== null      ? keyOrEdits : { [keyOrEdits]: val };    setValues((prev) => ({ ...prev, ...edits }));    window.parent.postMessage({ type: 'miaoda:tweaks:set-keys', edits }, '*');    // Same-window signal so in-page listeners (deck-stage rail thumbnails)    // can react — the parent message only reaches the host, not peers.    window.dispatchEvent(new CustomEvent('tweakchange', { detail: edits }));  }, []);
Start here · InstructionsSKILL.md
lark-apps
Lines connect the instruction file to its sections, not an observed execution order. Select a section to read the source. 2 more sections are available in the original file.

File reference map

References: 40
Files making referencesReferenced content
Lines show actual file references, not execution order. Select a node to highlight its connections and inspect the files and source locations. Dashed lines include files that still need locating.
Files and check records54 files

Coverage and gaps

  • creative-design/agents/assets/vision-probe.pngVerified non-program resource · 263 B
Content covered in each file

These are the source ranges included in this check, not a guarantee that every issue has been resolved.

  • SKILL.mdFull text included
  • creative-design/creative-design.mdFull text included
  • references/lark-apps-automation.mdFull text included
  • references/lark-apps-cache.mdFull text included
  • references/lark-apps-cloud-dev.mdFull text included
  • references/lark-apps-create.mdFull text included
  • references/lark-apps-db-execute.mdFull text included
  • references/lark-apps-db.mdFull text included
  • references/lark-apps-env-pull.mdFull text included
  • references/lark-apps-env.mdFull text included
  • references/lark-apps-file.mdFull text included
  • references/lark-apps-get.mdFull text included
  • references/lark-apps-git-credential.mdFull text included
  • references/lark-apps-html-publish.mdFull text included
  • references/lark-apps-init.mdFull text included
  • references/lark-apps-list.mdFull text included
  • references/lark-apps-local-dev.mdFull text included
  • references/lark-apps-observability.mdFull text included
  • references/lark-apps-openapi-key.mdFull text included
  • references/lark-apps-plugin-install.mdFull text included
  • references/lark-apps-plugin-list.mdFull text included
  • references/lark-apps-plugin-uninstall.mdFull text included
  • references/lark-apps-release-create.mdFull text included
  • references/lark-apps-release-get.mdFull text included
  • references/lark-apps-release-list.mdFull text included
  • references/lark-apps-role.mdFull text included
  • references/lark-apps-session-messages-list.mdFull text included
  • references/lark-apps-update.mdFull text included
  • references/lark-apps-user-id-convert.mdFull text included
  • creative-design/starter-components/android-frame.jsxFull text included
  • creative-design/starter-components/animations.jsxFull text included
  • creative-design/starter-components/browser-window.jsxFull text included
  • creative-design/starter-components/deck-stage.jsFull text included
  • creative-design/starter-components/design-canvas.jsxFull text included
  • creative-design/starter-components/ios-frame.jsxFull text included
  • creative-design/starter-components/macos-window.jsxFull text included
  • creative-design/starter-components/tweaks-panel.jsxFull text included
  • creative-design/assets/index.htmlFull text included
  • creative-design/references/animated-video.mdFull text included
  • creative-design/references/charts.mdFull text included
  • creative-design/references/data-report.mdFull text included
  • creative-design/references/frontend-design.mdFull text included
  • creative-design/references/hi-fi-design.mdFull text included
  • creative-design/references/interactive-prototype.mdFull text included
  • creative-design/references/make-a-deck.mdFull text included
  • creative-design/references/visual-exposure.mdFull text included
  • creative-design/references/wireframe.mdFull text included
  • references/lark-apps-access-scope-set.mdFull text included
  • creative-design/agents/fork-verifier-agent.mdFull text included
  • creative-design/agents/vision-probe-agent.mdFull text included
  • creative-design/references/aily.mdFull text included
  • creative-design/references/claude.mdFull text included
  • creative-design/references/codex.mdFull text included
  • references/lark-apps-access-scope-get.mdFull text included

This report is for the version above. We read the available code and instructions without running the skill or checking extra packages it installs. This is not a promise of safety: a different version or setup may behave differently.

  • SKILL.mdInstructions
  • creative-design/agents/fork-verifier-agent.mdSupporting file
  • creative-design/agents/vision-probe-agent.mdSupporting file
  • creative-design/assets/index.htmlSupporting file
  • creative-design/creative-design.mdSupporting file
  • creative-design/references/aily.mdSupporting file
  • creative-design/references/animated-video.mdSupporting file
  • creative-design/references/charts.mdSupporting file
  • creative-design/references/claude.mdSupporting file
  • creative-design/references/codex.mdSupporting file
  • creative-design/references/data-report.mdSupporting file
  • creative-design/references/frontend-design.mdSupporting file
  • creative-design/references/hi-fi-design.mdSupporting file
  • creative-design/references/interactive-prototype.mdSupporting file
  • creative-design/references/make-a-deck.mdSupporting file
  • creative-design/references/visual-exposure.mdSupporting file
  • creative-design/references/wireframe.mdSupporting file
  • creative-design/starter-components/android-frame.jsxScript
  • creative-design/starter-components/animations.jsxScript
  • creative-design/starter-components/browser-window.jsxScript
  • creative-design/starter-components/deck-stage.jsScript
  • creative-design/starter-components/design-canvas.jsxScript
  • creative-design/starter-components/ios-frame.jsxScript
  • creative-design/starter-components/macos-window.jsxScript
  • creative-design/starter-components/tweaks-panel.jsxScript
  • references/lark-apps-access-scope-get.mdSupporting file
  • references/lark-apps-access-scope-set.mdSupporting file
  • references/lark-apps-automation.mdSupporting file
  • references/lark-apps-cache.mdSupporting file
  • references/lark-apps-cloud-dev.mdSupporting file
  • references/lark-apps-create.mdSupporting file
  • references/lark-apps-db-execute.mdSupporting file
  • references/lark-apps-db.mdSupporting file
  • references/lark-apps-env-pull.mdSupporting file
  • references/lark-apps-env.mdSupporting file
  • references/lark-apps-file.mdSupporting file
  • references/lark-apps-get.mdSupporting file
  • references/lark-apps-git-credential.mdSupporting file
  • references/lark-apps-html-publish.mdSupporting file
  • references/lark-apps-init.mdSupporting file
  • references/lark-apps-list.mdSupporting file
  • references/lark-apps-local-dev.mdSupporting file
  • references/lark-apps-observability.mdSupporting file
  • references/lark-apps-openapi-key.mdSupporting file
  • references/lark-apps-plugin-install.mdSupporting file
  • references/lark-apps-plugin-list.mdSupporting file
  • references/lark-apps-plugin-uninstall.mdSupporting file
  • references/lark-apps-release-create.mdSupporting file
  • references/lark-apps-release-get.mdSupporting file
  • references/lark-apps-release-list.mdSupporting file
  • references/lark-apps-role.mdSupporting file
  • references/lark-apps-session-messages-list.mdSupporting file
  • references/lark-apps-update.mdSupporting file
  • references/lark-apps-user-id-convert.mdSupporting file

Operations mentioned in code and instructions

Run commands
SKILL.md:17In the instructions
妙搭应用是用户的个人资产,统一 `--as user`(见开头)。已有用户身份可用时直接执行业务命令,**不要为了预防权限问题主动重新登录**,否则可能中断原任务并触发不必要的设备授权。仅当 CLI 明确返回未登录或缺少本域 scope 时,一次性执行:
SKILL.md:19In the instructions
```bashlark-cli auth login --domain apps
SKILL.md:80In the instructions
```bash# 读取协作者和当前协作策略
Read keys or account settings
SKILL.md:38In the instructions
| 开发已有应用 / 初始化本地仓库(开发方式已定为本地后;先解析 app_id,勿 `+create` 新建) | `+init`(或手动 `+git-credential-init` + 原生 git)。**执行前必读** [`lark-apps-local-dev.md`](references/lark-apps-local-dev.md),含端到端流程和领域规则 | [`lark-apps-init.md`](references/lark-apps-init.md | 本地开发时 `.env.local` 损坏/丢失,重新拉取启动期环境变量 | `+env-pull` | [`lark-apps-env-pull.md`](references/lark-apps-env-pull.md) || 管理应用环境变量(查看/设置/删除) | `+env-list`, `+env-set`, `+env-delete` | [`lark-apps-env.md`](references/lark-apps-env.md) |
SKILL.md:51In the instructions
| 云端 Agent 生成/迭代应用(开发方式已定为云端后) | `+session-create` -> `+chat` -> `+session-get` | [`lark-apps-cloud-dev.md`](references/lark-apps-cloud-dev.md) || 管理妙搭应用开放 API Key(创建/查看/启停/重置/删除凭证;密钥仅 create/reset 一次性返回) | `+openapi-key-list/get/create/update/enable/disable/delete/reset` | [`lark-apps-openapi-key.md`](references/lark-apps-openapi-key.md) || 管理妙搭应用自动化触发器(定时/记录变更/Webhook/飞书审批四类触发器的查询/创建/更新/启停;Webhook URL·Token 一次性回显、不落盘) | `+automation-list/get/create/update/enable/disable` | [`lark-apps-automation.md`](references/lark-apps-automation.md) |
SKILL.md:118In the instructions
- 发布态链接来源:`+release-get` 轮询 `finished` 给 `online_url` / `failed` 给 `error_logs`(html / frontend / full_stack 统一走 `+release-get`)。- html 应用的主链路是创意模式开发方式:按 [`lark-apps-local-dev.md`](references/lark-apps-local-dev.md) 初始化仓库、在仓库内产出 HTML 及关联文件,并通过 git commit / git push / `+release-create` / `+release-get` 发布部署。任何 git 操作(clone / pull / push)报错时,先执行 `lark-cli apps +git-credential-init --app-id <app_id> --as user` 刷新本地 Git 凭证,再重试原 git 命令。如果刷新凭证也失败,**停止并向用户报告**:原始 git 错误、凭证刷新失败原因,以及是否可能是当前环境(操作系统、沙箱)限制导致(如 macOS Keychain 在沙箱中不可用、Linux 加密文件目录不可写等)。不要改走 `+html-publish`,也不要把 `+html-publish` 当作本地开发链路的 fallback。- 创意模式(html)应用的链接格式为 `https://{租户域名}/page/{meta_token}`,**开发态和发布态是同一个链接**(区别于 full_stack 应用两者分开)。此链接形似飞书文档链接。`+get --app-id <meta_token>` 可获取应用信息(含 `app_id`),`+get --app-id <app_id>` 可获取 `meta_token`。看到 `/page/xxx` 链接时,它是妙搭创意模式应用,不要当成飞书文档跳过 
Connect to websites
creative-design/starter-components/animations.jsx:587In the code
        {playing ? (          <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">            <path d="M3.33333 1.33398C2.59695 1.33398 2 1.93094 2 2.66732V13.334C2 14.0704 2.59695 14.6673 3.33333 14.6673H4.66667C5.40305 14.6673 6 14.0704 6 13.334V2.66732C6 1.93094 5.40305 1.33398 4.66667 1.33398H3.33333Z" fill="currentC 
creative-design/starter-components/animations.jsx:592In the code
        ) : (          <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">            <path d="M14.0489 9.13127C14.873 8.60116 14.873 7.39754 14.0489 6.86743L4.74461 0.882617C3.84764 0.305661 2.66699 0.948902 2.66699 2.01454V13.9842C2.66699 15.0498 3.84764 15.693 4.74461 15.1161L14.0489 9.13127Z" fill="currentCol 
creative-design/starter-components/deck-stage.js:864In the code
      Promise.all(pending.map((href) =>        fetch(href).then((r) => (r.ok ? r.text() : '')).catch(() => '')          .then((text) => { this._xoCssCache.set(href, text); })
Read files
creative-design/starter-components/design-canvas.jsx:371In the code
    const onDocDown = (e) => {      if (rootRef.current && !rootRef.current.contains(e.target)) setOpen(false);    };
creative-design/starter-components/design-canvas.jsx:399In the code
          className="dc-zoom-btn dc-zoom-trigger"          onClick={() => setOpen((o) => !o)}        >
creative-design/starter-components/design-canvas.jsx:409In the code
            {DC_ZOOM_PRESETS.map((p) => (              <button type="button" key={p} onClick={() => { onZoomTo(p); setOpen(false); }}>                <span>{p}%</span>
Change files
creative-design/starter-components/design-canvas.jsx:226In the code
// Persist a sidecar file back to the host.function miaodaWriteFile(path, content) {  try {
creative-design/starter-components/design-canvas.jsx:265In the code
    const t = setTimeout(() => {      miaodaWriteFile(DC_STATE_FILE, JSON.stringify({ sections: state.sections })).catch(() => {});    }, 250);
Install extra software packages
references/lark-apps-local-dev.md:16In the instructions
`+create(full_stack)` -> `+init`(或手动 `+git-credential-init` + `git clone`)-> 读仓库 Skill -> `npm install && npm run dev` -> 按需 `+db-*` 调库 -> 非自动化改动按本页 commit/push/release;包含自动化 handler 时,在任何 release 前转到 [automation SOP](lark-apps-automation.md),由它接管状态门禁和完整发布。
references/lark-apps-local-dev.md:28In the instructions
cd ./approval-appnpm installnpm run dev
references/lark-apps-local-dev.md:53In the instructions
cd ./json-toolnpm installnpm run dev
Lines read
9,779
File checksum (to compare versions)
c607e0c1521bc701610527147736214bfd8975eff57158e8ca8e95cb16185162