registry  /  @tianshu-ai/tianshu  /  0.4.25

@tianshu-ai/tianshu@0.4.25

An open AI agent platform with a sidecar browser. Built in public.

AI Security Review

scanned 4d ago · by lpm-firewall-ai

No confirmed malicious attack surface was established by source inspection. Risky primitives are explicit product features for an AI agent platform: CLI setup, sandboxed workers, web fetch/search, model proxying, and WeChat media integration.

Static reason
One or more suspicious static signals were detected.; previous stored version diff introduced dangerous source
Trigger
User-invoked CLI commands or configured runtime plugin/worker actions.
Impact
No evidence of unconsented install-time execution, credential harvesting, exfiltration, destructive behavior, or AI-agent control hijack.
Mechanism
Package-aligned CLI, plugin, sandbox, and network integrations.
Rationale
The scanner-highlighted behaviors are real but align with documented, user-invoked functionality of an AI agent platform and its plugins. I found no install-time execution, covert persistence, credential/env harvesting, hidden exfiltration, destructive action, or unconsented agent-control mutation.
Evidence
package.jsonbin/tianshu.mjspackages/server/dist/cli.jspackages/server/dist/setup/start-server.jspackages/server/dist/setup/launchd.jsplugins/workboard/dist/worker/opencode-worker.jsplugins/web-search/dist/tools/web-fetch.jsplugins/wechat/dist/ilink-media.jsplugins/wechat/dist/ilink-api.js
Network endpoints8
ilinkai.weixin.qq.comnovac2c.cdn.weixin.qq.com/c2csearch.parallel.ai/mcpmcp.exa.ai/mcpraw.githubusercontent.com/tianshu-ai/plugin-registry/main/catalog.jsonapi.anthropic.comapi.openai.comgenerativelanguage.googleapis.com

Decision evidence

public snapshot
AI called this Clean at 84.0% confidence as Benign with medium false-positive risk.
Evidence for block
  • plugins/workboard/dist/worker/opencode-worker.js can user-run `npm i -g opencode-ai@1.17.13` and `opencode run` inside a configured sandbox worker.
  • packages/server/dist/setup/start-server.js and launchd.js can write launchd plists and .env during explicit `tianshu setup/start`.
  • plugins/wechat/dist/ilink-media.js reads caller-supplied local files and uploads encrypted media to Tencent iLink/CDN endpoints.
Evidence against
  • package.json has no install/postinstall lifecycle; only prepublishOnly build, so install-time execution is not present.
  • bin/tianshu.mjs only resolves and imports packages/server/dist/cli.js when the user invokes the CLI.
  • plugins/web-search/dist/tools/web-fetch.js blocks localhost, private, link-local, and metadata hosts before fetching and after redirects.
  • OpenCode worker is gated by configured workboard agents plus sandbox.shell and host.opencodeProxy capabilities, not automatic import/install behavior.
  • launchd persistence requires explicit CLI setup/start flow with prompts and writes a product service plist.
  • Network endpoints are package-aligned: model providers, Tencent iLink/CDN, plugin catalog, local health checks, and optional search MCP providers.
Behavioral surface
Source
ChildProcessCryptoDynamicRequireEnvironmentVarsFilesystemNativeBindingsNetworkShellWebSocket
Supply chain
HighEntropyStringsMinifiedObfuscatedProtestwareUrlStrings
Manifest
WildcardDependency
scanned 333 file(s), 8.15 MB of source, external domains: 127.0.0.1, api.anthropic.com, api.openai.com, dashscope.aliyuncs.com, example.com, generativelanguage.googleapis.com, github.com, host.docker.internal, host.openshell.internal, ilinkai.weixin.qq.com, mcp.exa.ai, novac2c.cdn.weixin.qq.com, opencode.ai, raw.githubusercontent.com, react.dev, reactrouter.com, registry.npmjs.org, search.parallel.ai, www.apple.com, www.docker.com, www.w3.org

Source & flagged code

5 flagged · loading source
bin/tianshu.mjsView file
29L30: const { main } = await import(distEntry); L31: const code = await main(process.argv.slice(2));
Medium
Dynamic Require

Package source references dynamic require/import behavior.

bin/tianshu.mjsView on unpkg · L29
plugins/wechat/dist/ilink-media.jsView file
18// L19: // The CDN URL itself is `https://novac2c.cdn.weixin.qq.com/c2c` L20: // (platform constant, matches what Tencent's plugin hard-codes). ... L76: }); L77: const resp = JSON.parse(raw); L78: if (resp.ret && resp.ret !== 0) { ... L92: headers: { "Content-Type": "application/octet-stream" }, L93: body: new Uint8Array(opts.ciphertext), L94: });
Low
Weak Crypto

Package source references weak cryptographic algorithms.

plugins/wechat/dist/ilink-media.jsView on unpkg · L18
packages/server/dist/setup/start-server.jsView file
21import fs from "node:fs"; L22: import net from "node:net"; L23: import os from "node:os"; ... L36: // typically run `tianshu setup` from their home dir, so L37: // process.cwd() won't be the repo. Walk up from this module's L38: // location until we hit a package.json named L39: // '@tianshu-ai/tianshu' (or run out of parents). ... L50: ? path.join(repoRoot, ".env") L51: : path.join(process.env.TIANSHU_HOME ?? L52: path.join(process.env.HOME ?? "", ".tianshu"), ".env")); ... L137: if (!r.ok) { L138: p.log.error(`launchctl kickstart failed: ${r.stderr ?? "(unknown)"}`);
Medium
Install Persistence

Source writes installer persistence such as shell profile or service configuration.

packages/server/dist/setup/start-server.jsView on unpkg · L21
plugins/web-search/dist/tools/web-fetch.jsView file
1// `web_fetch(url, extractMode?, maxChars?)`. L2: // ... L14: // L15: // SSRF: we block private / loopback / link-local / metadata hosts L16: // before fetching AND re-check after redirects, so an agent can't ... L93: const ctype = res.headers.get("content-type") ?? ""; L94: const body = await res.text(); L95: let out; ... L110: text, L111: data: { L112: url,
High
Cloud Metadata Access

Source reaches cloud instance metadata or link-local credential endpoints.

plugins/web-search/dist/tools/web-fetch.jsView on unpkg · L1
plugins/workboard/dist/worker/opencode-worker.jsView file
matchType = previous_version_dangerous_delta matchedPackage = @tianshu-ai/tianshu@0.4.24 matchedIdentity = npm:QHRpYW5zaHUtYWkvdGlhbnNodQ:0.4.24 similarity = 0.975 summary = stored previous version shares package body but lacks this dangerous source file
Critical
Previous Version Dangerous Delta

This package version adds a dangerous source file absent from the previous stored version.

plugins/workboard/dist/worker/opencode-worker.jsView on unpkg

Findings

1 Critical1 High7 Medium7 Low
CriticalPrevious Version Dangerous Deltaplugins/workboard/dist/worker/opencode-worker.js
HighCloud Metadata Accessplugins/web-search/dist/tools/web-fetch.js
MediumDynamic Requirebin/tianshu.mjs
MediumNetwork
MediumEnvironment Vars
MediumInstall Persistencepackages/server/dist/setup/start-server.js
MediumProtestware
MediumStructural Risk Force Deep Review
MediumWildcard Dependency
LowNon Install Lifecycle Scripts
LowScripts Present
LowWeak Cryptoplugins/wechat/dist/ilink-media.js
LowFilesystem
LowObfuscated
LowHigh Entropy Strings
LowUrl Strings