Supporting evidence · Detailed local review
Langfuse CLI beta: local manual security review
- Source
- Local code review · unchanged source
- Last verified
- October 16, 2026
- Reading mode
- Read; optional download
- Linked workflow effects
- Download saves a Markdown file; no CLI execution or account changes
- Outcome
- Inspect the complete findings before downloading
Reviewed 2026-10-16 (America/Denver). Exact artifact: @langfuse/cli@1.3.0-beta.2, SHA-256 55eadceb86b179dcb149cfa738aea8ed5df415a791c65a0f091ab7a3c3e404a7. This is a targeted local Codex review of the inert npm artifact, not a comprehensive audit or external independent audit. No package installation, target execution, authentication, credential reading, or external semantic transmission occurred.
Assessment
No malicious behavior was identified in the reviewed CLI skill-management flows. The static scanner's high-severity leads are consistent with intended API functionality: repeated DELETE endpoint descriptions, explicit credential-file examples, and API cookie/BasicAuth construction. Its raw 100/CRITICAL result remains an unmodified preliminary scanner output, not a confirmed malware finding. Installation should remain deferred until the user decides to proceed with this exact reviewed version and a controlled trial; this report does not clear arbitrary future beta releases or skills downloaded through the CLI.
The CLI includes useful content-integrity and filesystem defenses. The more consequential residual risks are configuration mistakes, publishing unintended local text, trusting the selected Langfuse project and mutable labels, and giving downloaded skill instructions authority inside an agent.
Findings and caveats
LF-SEC-01 — P2 configuration caveat: a stale base URL can shadow the intended host. dist/cli.js:187, pp() and mx(): an explicit --env file is merged over process environment values of the same key. The final host precedence is --host > LANGFUSE_BASE_URL > LANGFUSE_HOST > https://cloud.langfuse.com. Thus, an inherited LANGFUSE_BASE_URL can still shadow a LANGFUSE_HOST in the chosen env file. The CLI accepts arbitrary self-hosted URLs and does not require HTTPS or bind keys to a region/project before constructing BasicAuth. Set the exact intended HTTPS host explicitly and keep both host variables consistent; avoid passing secret keys as CLI flags because command-line arguments can be exposed in history/process listings. The CLI reads an env file only when --env is explicitly supplied; no automatic project-wide credential search was found.
LF-SEC-02 — P2 publishing caveat: push selects all supported text, with no secret scan or gitignore filtering. dist/cli.js:183, Uk(), Bk(), Hk(), Qk(): push recursively reads every regular file beneath a selected skill directory, subject to a text-extension allowlist, UTF-8 validation, maximum 200 files, and 1,000,000 UTF-8 bytes in total. It rejects unsupported files rather than silently skipping them. Extensions include common documents, configs, JSON/YAML, logs, and executable-source text such as sh/py/js. There is no filename denylist, secret detector, .gitignore, or ignore-file evaluation in this selection flow. A token in notes.txt, .settings.json, or another allowed file can be published. .env/credentials.env have an unsupported env extension and would make a skill invalid, but that is not a general secret-protection mechanism. Use a staged, reviewed directory containing only intended skill files. No local credential files were opened for this review.
LF-SEC-03 — P2 trust/reproducibility caveat: the lockfile pins selection, not project identity or independently trusted content. dist/cli.js:178, schema kh/Al and constant ft; :179, Il(): the actual filename is langfuse-skills.lock.json. Entries store only path, name, selector, and numeric version; no host, project ID, file digest, or publisher signature is stored. Bare skills install restores locked numeric versions; skills update follows saved labels while explicit numeric selectors remain pinned. Switching host or credentials can resolve the same skill name/version to another project's content. Download hashes are checked against the manifest returned by the same authenticated service; this provides useful consistency/integrity checks but does not establish publisher trust, semantic safety, or source-build provenance. Treat Git review, project/host selection, permissions to move labels, and review of each skill as part of the trust boundary. CLI code cannot prove server-side immutability, authorization enforcement, or audit-log behavior.
LF-SEC-04 — P3 operational caveat: push dry-run is authenticated and networked. dist/cli.js:183, sp() → Xk() → bl(); :178, Fe(): skills push ... --dry-run reads and hashes local skill files, then performs authenticated GETs for the current latest manifest so it can compare changes. In the reviewed branches, it does not POST content, PATCH labels, or write the lockfile. It is a useful publishing preview, not an offline command. It still contacts the configured host and reveals requested skill names to that host.
LF-SEC-05 — P3 operational caveat: force replacement and bulk operations are consequential. dist/cli.js:179, jl(); :183, Zh()/Mh() and sp(): installation/update compares local files with the requested snapshot and, for label updates, the previous locked version. Divergent local files are rejected unless --force is supplied. Replacement stages a new directory, renames the old one to a temporary backup, renames the staging directory into place, then removes the backup. --force can therefore discard extra/edited local files in the target directory. Multi-skill installs and pushes are sequential rather than a transaction; earlier successful writes/publications remain when a later operation fails. Push creates a complete new snapshot; files omitted from that snapshot become removed in that new version. Do not use force or bulk publication until the exact file set and labels have been reviewed.
Verified defenses in the reviewed code
- Downloaded contents are verified.
dist/cli.js:183,jh(): checks UTF-8 byte length and SHA-256 (canonical base64) against each manifest entry before writing._l()at:178rejects unexpected, duplicated, or missing content hashes. Staging is rechecked byir()before replacing the destination. - Destination defaults and protections.
Sl()at:178defaults to.agents/skills; new named skills are placed under that directory.ur()/Pl()at:179resolve real paths, require locked installations to remain within the project, and reject overlapping installation directories. Explicit--no-lockfileopts out of lockfile/project-directory restrictions and allows an outside-project destination. - Manifest paths are validated.
Th()/Ml()at:183require a rootSKILL.md, reject duplicated normalized paths, backslashes, NUL, empty/dot/dotdot segments, and resolved paths outside the intended root. Fresh staging uses random names and exclusivewxfile creation; installed files are chmod 0644. This writes files without executing their contents. Case/conflict problems are detected by rechecking the staged snapshot. - Push rejects symlinks and nonregular files.
ep(),Qf(),Uk(),Bk()at:183use lstat/realpath containment checks, reject symlink directories/files, and open files withO_NOFOLLOW.Kk()requires valid root skill frontmatter and disables YAML aliases (maxAliasCount:0). Concurrent local filesystem mutation was not dynamically tested; checks should not be treated as a race-proof sandbox. - Skill API redirects are rejected.
Fe()at:178sends BasicAuth only to the configured skills endpoint and setsredirect:"error", plus a request timeout. The separate general API client and health/get-skill requests use fetch's default redirect behavior. - No hidden telemetry or subprocess launch was identified. Four fetch callsites exist: the configured general API (
:139), configured skills API (:178), configured health endpoint (:187), and public GitHub rawget-skilldownload (:187). No child_process/shell execution imports were found.--curlformats a command for output; it does not run it, and authorization is redacted unless--show-secretsis explicitly selected. Bundled Zod has a Function constructor used for generated validation code; no skill-content-to-shell execution path was identified. - Separate get-skill trust caveat.
get-skilldownloads the mutable public GitHub main-branch Langfuse SKILL.md and prints it; it neither authenticates to Langfuse nor installs it. It has no digest pin or semantic scan. Review that raw instruction artifact before loading it into an agent.
Static finding adjudication
- 128 TM1 high matches are DELETE operation paths across seven bundled API contracts, not automatic deletion calls. Actual API commands require explicit user command selection.
- Two PE3 high matches are documented explicit
--env .envusage, not evidence of credential hunting. - YR1 at
dist/cli.js:139matches generic API cookie parameter serialization adjacent to BasicAuth creation. It does not access browser cookie stores in the reviewed function. - RP1 treats a README unpinned
npx @langfuse/clicommand as an MCP-server reference; it is a CLI example. Pinning the exact beta version is still appropriate for reproducibility. - E1 is an API documentation URL; AS3 is the public Langfuse skill path used by get-skill. Neither establishes covert exfiltration.
- Eight SC1 findings concern development dependency version ranges. The installed artifact declares no external runtime dependencies, but includes bundled third-party code. The failed OSV lookup means current vulnerability coverage is incomplete.
Review coverage and exclusions
Read the executable entrypoint, package manifest, README, all security-relevant skill/API/network/config/file-operation functions in the bundled CLI, and statically enumerated bundled contract operations, import surfaces, URL strings, and fetch callsites. The reviewed artifact has 13 files and 1,181,168 unpacked bytes. Third-party bundled YAML/Zod internals were inspected selectively; they were not audited in full or rebuilt from source. No runtime tests, sandbox adversarial filesystem tests, server-side Langfuse implementation/authorization tests, source-to-tarball build reconciliation, complete dependency SBOM/vulnerability scan, or review of future remotely installed skills occurred. No claim of a comprehensive audit is warranted.
For a later trial, prefer an exact version pin, an explicit verified HTTPS host, a disposable local project, a single reviewed skill at a numeric version, and review of lockfile/content changes before agent loading. The installation decision and any use of credentials are separate authorized actions and were not performed here.