larksuite/cli · error

unsafe proxy plugin config %q: %w

Error message

unsafe proxy plugin config %q: %w

What it means

transport.Load rejects the proxy plugin config with this error when binding.AssertSecurePath fails its security audit. Because this config decides where ALL outbound CLI traffic egresses and which extra CA is trusted, Load refuses files that a local attacker could tamper with: symlinked paths, foreign ownership, or group/world-writable files (readability by others is allowed since the file is not secret). This prevents credential traffic from being redirected through a hostile proxy.

Source

Thrown at internal/transport/config.go:103

					loadCfg = nil
				}
				loadErr = nil
				return
			}
			loadErr = fmt.Errorf("failed to stat proxy plugin config %q: %w", p, err)
			return
		}
		// Security hardening: this config dictates where ALL outbound CLI traffic
		// egresses and which extra CA is trusted, so a file another local user or
		// process can tamper with (symlink, foreign owner, group/world-writable)
		// could redirect credential traffic. Audit it the same way the CA file is.
		safePath, err := binding.AssertSecurePath(binding.AuditParams{
			TargetPath:            p,
			Label:                 ConfigFileName,
			AllowReadableByOthers: true, // config is not a secret; only writability/owner/symlink matter
		})
		if err != nil {
			loadErr = fmt.Errorf("unsafe proxy plugin config %q: %w", p, err)
			return
		}
		b, err := vfs.ReadFile(safePath)
		if err != nil {
			loadErr = fmt.Errorf("failed to read proxy plugin config %q: %w", p, err)
			return
		}
		var fileCfg Config
		if err := json.Unmarshal(b, &fileCfg); err != nil {
			loadErr = fmt.Errorf("invalid proxy plugin config %q: %w", p, err)
			return
		}

		// Merge: file base + env overrides.
		if cfg == nil {
			cfg = &fileCfg
		} else {
			*cfg = fileCfg

View on GitHub (pinned to 7fd6ef3c07)

Solutions

  1. Replace the symlink with a real regular file: rm the symlink and cp the target content into place
  2. Fix ownership so the file belongs to the current user: chown $(id -u) ~/.lark-cli/proxy_config.json
  3. Remove insecure write bits: chmod go-w ~/.lark-cli/proxy_config.json (and chmod go-w ~/.lark-cli if the dir is writable by others)
  4. Prefer proxy env vars (LARKSUITE_CLI_PROXY_ENABLE/ADDRESS, LARKSUITE_CLI_CA_PATH) instead of the file if a symlinked config is unavoidable

Example fix

// before
lrwxrwxrwx proxy_config.json -> dotfiles/proxy_config.json

// after
rm ~/.lark-cli/proxy_config.json
cp ~/dotfiles/proxy_config.json ~/.lark-cli/proxy_config.json
chmod 644 ~/.lark-cli/proxy_config.json
Defensive patterns

Strategy: validation

Validate before calling

func isSafeProxyConfig(p string) error {
    info, err := os.Lstat(p) // Lstat: fails on symlink targets
    if err != nil { return err }
    if info.Mode()&os.ModeSymlink != 0 || info.Mode()&os.ModeType != 0 {
        return fmt.Errorf("%s must be a regular file, not a symlink", p)
    }
    if info.Mode().Perm()&0o022 != 0 {
        return fmt.Errorf("%s must not be group/world writable", p)
    }
    if st, ok := info.Sys().(*syscall.Stat_t); ok && st.Uid != uint32(os.Getuid()) {
        return fmt.Errorf("%s must be owned by the current user", p)
    }
    return nil
}

Type guard

func configIsRegularAndOwned(fi os.FileInfo, uid int) bool {
    return fi.Mode().IsRegular() &&
        fi.Mode().Perm()&0o022 == 0 &&
        stUid(fi) == uid
}

Try / catch

cfg, err := transport.Load()
if err != nil && strings.Contains(err.Error(), "unsafe proxy plugin config") {
    p := extractQuotedPath(err.Error())
    // replace symlink with real file and tighten perms, then retry
    _ = os.Remove(p)
    _ = os.WriteFile(p, nil, 0o644)
    os.Chmod(p, 0o644)
}

Prevention

When it happens

Trigger: Loading the CLI transport when ~/.lark-cli/proxy_config.json exists but is a symlink (or inside a symlinked component), owned by another user (not the current UID), or has group-write/world-write permission bits set; AssertSecurePath returns the violation and Load wraps it with the path.

Common situations: Dotfile managers (stow, GNU stow, chezmoi, symlinking ~/.lark-cli into a repo) turn the config into a symlink; a shared machine where an admin or installer created the file as root with umask 000; restoring backups that lost original ownership/permissions; running the CLI as a service account while the file was created by the login user.

Related errors


AI-assisted analysis of larksuite/cli@7fd6ef3c07 (2026-09-04). Data as JSON: /api/errors/48fbbf6e4faf41bc. Report an issue: GitHub.