antigravity-ai-kit 3.2.0 → 3.3.1

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.
package/lib/io.js CHANGED
@@ -68,7 +68,44 @@ function readJsonSafe(filePath, defaultValue = null) {
68
68
  }
69
69
  }
70
70
 
71
+ /**
72
+ * Recursively copies a directory, skipping symbolic links for security.
73
+ *
74
+ * Symlinks are skipped because they could point outside the intended
75
+ * scope (e.g., outside .agent/), enabling path traversal attacks.
76
+ *
77
+ * @param {string} src - Source directory path
78
+ * @param {string} dest - Destination directory path
79
+ * @returns {void}
80
+ */
81
+ function safeCopyDirSync(src, dest) {
82
+ if (!fs.existsSync(dest)) {
83
+ fs.mkdirSync(dest, { recursive: true });
84
+ }
85
+
86
+ const entries = fs.readdirSync(src, { withFileTypes: true });
87
+
88
+ for (const entry of entries) {
89
+ const srcPath = path.join(src, entry.name);
90
+ const destPath = path.join(dest, entry.name);
91
+
92
+ // Security: skip symlinks to prevent path traversal
93
+ const stat = fs.lstatSync(srcPath);
94
+ if (stat.isSymbolicLink()) {
95
+ log.debug('Skipping symlink for security', { path: srcPath });
96
+ continue;
97
+ }
98
+
99
+ if (entry.isDirectory()) {
100
+ safeCopyDirSync(srcPath, destPath);
101
+ } else {
102
+ fs.copyFileSync(srcPath, destPath);
103
+ }
104
+ }
105
+ }
106
+
71
107
  module.exports = {
72
108
  writeJsonAtomic,
73
109
  readJsonSafe,
110
+ safeCopyDirSync,
74
111
  };
@@ -16,7 +16,7 @@ const fs = require('fs');
16
16
  const path = require('path');
17
17
 
18
18
  const { AGENT_DIR, ENGINE_DIR, PLUGINS_DIR, HOOKS_DIR } = require('./constants');
19
- const { writeJsonAtomic } = require('./io');
19
+ const { writeJsonAtomic, safeCopyDirSync } = require('./io');
20
20
  const { createLogger } = require('./logger');
21
21
  const log = createLogger('plugin-system');
22
22
  const PLUGINS_REGISTRY = 'plugins-registry.json';
@@ -289,7 +289,7 @@ function installPlugin(pluginPath, projectRoot) {
289
289
  for (const skillDir of (manifest.skills || [])) {
290
290
  const src = path.join(pluginPath, 'skills', skillDir);
291
291
  const dest = path.join(projectRoot, AGENT_DIR, 'skills', skillDir);
292
- copyDirSync(src, dest);
292
+ safeCopyDirSync(src, dest);
293
293
  installed.skills++;
294
294
  }
295
295
 
@@ -593,31 +593,7 @@ function copyFileSync(src, dest) {
593
593
  fs.copyFileSync(src, dest);
594
594
  }
595
595
 
596
- /**
597
- * Recursively copies a directory.
598
- *
599
- * @param {string} src - Source directory
600
- * @param {string} dest - Destination directory
601
- * @returns {void}
602
- */
603
- function copyDirSync(src, dest) {
604
- if (!fs.existsSync(dest)) {
605
- fs.mkdirSync(dest, { recursive: true });
606
- }
607
-
608
- const entries = fs.readdirSync(src, { withFileTypes: true });
609
596
 
610
- for (const entry of entries) {
611
- const srcPath = path.join(src, entry.name);
612
- const destPath = path.join(dest, entry.name);
613
-
614
- if (entry.isDirectory()) {
615
- copyDirSync(srcPath, destPath);
616
- } else {
617
- fs.copyFileSync(srcPath, destPath);
618
- }
619
- }
620
- }
621
597
 
622
598
  module.exports = {
623
599
  validatePlugin,
@@ -262,6 +262,12 @@ function collectAllFiles(dirPath) {
262
262
  for (const entry of entries) {
263
263
  const fullPath = path.join(dirPath, entry.name);
264
264
 
265
+ // Security: skip symlinks to prevent path traversal
266
+ const stat = fs.lstatSync(fullPath);
267
+ if (stat.isSymbolicLink()) {
268
+ continue;
269
+ }
270
+
265
271
  if (entry.isDirectory() && entry.name !== 'node_modules' && entry.name !== '.git') {
266
272
  files.push(...collectAllFiles(fullPath));
267
273
  } else if (entry.isFile()) {
package/lib/updater.js CHANGED
@@ -27,6 +27,7 @@ const PRESERVED_FILES = new Set([
27
27
  /** Directories whose contents should never be overwritten */
28
28
  const PRESERVED_DIRS = new Set([
29
29
  'decisions',
30
+ 'contexts',
30
31
  ]);
31
32
 
32
33
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "antigravity-ai-kit",
3
- "version": "3.2.0",
3
+ "version": "3.3.1",
4
4
  "description": "🚀 Trust-Grade AI development framework with a 29-module runtime engine — 19 Agents, 32 Skills, 31 Commands, 14 Workflows, 327 Tests. Workflow enforcement, task governance, agent reputation, self-healing, and skill marketplace.",
5
5
  "main": "bin/ag-kit.js",
6
6
  "bin": {