@dreamlogic-ai/cli 2.0.4 → 2.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -47,9 +47,15 @@ export async function updateCommand(opts) {
47
47
  continue;
48
48
  }
49
49
  const normalizeVer = (v) => v.replace(/^v/, "");
50
- if (normalizeVer(local.version) === normalizeVer(remote.latest_version)) {
50
+ const localVer = normalizeVer(local.version);
51
+ const remoteVer = normalizeVer(remote.latest_version);
52
+ if (localVer === remoteVer) {
51
53
  ui.line(`${chalk.green("✓")} ${remote.name} ${local.version} — 已是最新`);
52
54
  }
55
+ else if (remoteVer < localVer) {
56
+ // R4-C02 FIX: Block version downgrade attacks
57
+ ui.line(`${ui.warn("⚠")} ${remote.name} — 服务器版本 ${remote.latest_version} 低于本地 ${local.version}(可能的降级攻击,已跳过)`);
58
+ }
53
59
  else {
54
60
  ui.line(`${chalk.yellow("⬆")} ${chalk.bold(remote.name)} ${local.version} → ${chalk.green(remote.latest_version)}`);
55
61
  updates.push({
@@ -90,6 +90,11 @@ export function registerSkillWithAgents(skillId, skillPath, agents) {
90
90
  return [{ agent: "all", path: "", status: "error", error: "Invalid skill ID" }];
91
91
  }
92
92
  const resolvedSkillPath = resolve(skillPath);
93
+ // R4-H02 FIX: Validate skillPath is within user home directory
94
+ const home = homedir();
95
+ if (!resolvedSkillPath.startsWith(resolve(home))) {
96
+ return [{ agent: "all", path: "", status: "error", error: "Skill path must be within home directory" }];
97
+ }
93
98
  // Deduplicate by resolved dir (universal agents share .agents/skills)
94
99
  const seenDirs = new Set();
95
100
  for (const agent of agents) {
@@ -78,8 +78,9 @@ export function getApiKey() {
78
78
  /** R1-07: Enforce HTTPS (localhost/127.0.0.1 exempt for dev) */
79
79
  export function getServer() {
80
80
  const url = process.env.DREAMLOGIC_SERVER || loadConfig()?.server || DEFAULT_SERVER;
81
- // R1-07 + R2-03: HTTPS enforcement (localhost/127.0.0.1 exempt with strict boundary)
82
- if (!/^https:\/\//i.test(url) && !/^http:\/\/(localhost|127\.0\.0\.1)(:[0-9]+)?(\/|$)/i.test(url)) {
81
+ // R1-07 + R2-03 + R4-H06: HTTPS enforcement (localhost/127.x.x.x/[::1] exempt)
82
+ const LOCALHOST_RE = /^http:\/\/(localhost|127(?:\.\d{1,3}){3}|\[::1\]|0\.0\.0\.0)(:[0-9]+)?(\/|$)/i;
83
+ if (!/^https:\/\//i.test(url) && !LOCALHOST_RE.test(url)) {
83
84
  throw new Error("Server URL must use HTTPS (http://localhost allowed for dev)");
84
85
  }
85
86
  return url;
@@ -6,7 +6,7 @@
6
6
  * R1-03: Zip bomb protection (total size + entry count limits)
7
7
  * R1-10: SIGINT/SIGTERM cleanup handler
8
8
  */
9
- import { createWriteStream, existsSync, mkdirSync, readdirSync, renameSync, rmSync, statSync } from "fs";
9
+ import { createWriteStream, existsSync, lstatSync, mkdirSync, readdirSync, renameSync, rmSync, statSync, unlinkSync } from "fs";
10
10
  import { join, normalize, resolve as pathResolve, sep } from "path";
11
11
  import { Transform as TransformStream } from "stream";
12
12
  import yauzl from "yauzl";
@@ -309,7 +309,19 @@ function extractZip(buffer, targetDir) {
309
309
  });
310
310
  const writeStream = createWriteStream(fullPath);
311
311
  readStream.pipe(countingStream).pipe(writeStream);
312
- writeStream.on("finish", () => zipfile.readEntry());
312
+ writeStream.on("finish", () => {
313
+ // R4-C01 FIX: Post-write symlink verification (defense-in-depth)
314
+ try {
315
+ if (lstatSync(fullPath).isSymbolicLink()) {
316
+ unlinkSync(fullPath);
317
+ zipfile.close();
318
+ reject(new Error(`Post-extract symlink detected: ${entry.fileName}`));
319
+ return;
320
+ }
321
+ }
322
+ catch { /* stat failed = file doesn't exist, ok to continue */ }
323
+ zipfile.readEntry();
324
+ });
313
325
  writeStream.on("error", (e) => { zipfile.close(); reject(e); });
314
326
  countingStream.on("error", (e) => { zipfile.close(); reject(e); });
315
327
  readStream.on("error", (e) => { zipfile.close(); reject(e); });
package/dist/types.d.ts CHANGED
@@ -33,6 +33,6 @@ export interface InstalledRegistry {
33
33
  export declare const DEFAULT_SERVER = "https://skill.dreamlogic-claw.com";
34
34
  export declare const DEFAULT_INSTALL_DIR_NAME = "dreamlogic-skills";
35
35
  export declare const CONFIG_DIR_NAME = ".dreamlogic";
36
- export declare const CLI_VERSION = "2.0.4";
36
+ export declare const CLI_VERSION = "2.0.5";
37
37
  export declare const CLI_NAME = "Dreamlogic CLI";
38
38
  export declare const CLI_AUTHOR = "Dreamlogic-ai by MAJORNINE";
package/dist/types.js CHANGED
@@ -2,6 +2,6 @@
2
2
  export const DEFAULT_SERVER = "https://skill.dreamlogic-claw.com";
3
3
  export const DEFAULT_INSTALL_DIR_NAME = "dreamlogic-skills";
4
4
  export const CONFIG_DIR_NAME = ".dreamlogic";
5
- export const CLI_VERSION = "2.0.4";
5
+ export const CLI_VERSION = "2.0.5";
6
6
  export const CLI_NAME = "Dreamlogic CLI";
7
7
  export const CLI_AUTHOR = "Dreamlogic-ai by MAJORNINE";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dreamlogic-ai/cli",
3
- "version": "2.0.4",
3
+ "version": "2.0.5",
4
4
  "description": "Dreamlogic AI Skill Manager — Install, update and manage AI agent skills",
5
5
  "type": "module",
6
6
  "bin": {