codegate-ai 0.14.2 → 0.14.3

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/dist/cli.js CHANGED
@@ -296,7 +296,18 @@ function addScanCommand(program, version, deps) {
296
296
  ? true
297
297
  : (baseConfig.workflow_audits?.enabled ?? false),
298
298
  },
299
- scan_user_scope: options.includeUserScope === true ? true : (baseConfig.scan_user_scope ?? false),
299
+ // When the target was a single local file that got staged into a
300
+ // temp dir (explicitCandidates set), walking the full user-scope
301
+ // tree is off-target: the user asked to scan one file, not their
302
+ // whole home. Leaving user-scope on here let sibling findings
303
+ // (e.g. `~/.agents/skills/*/SKILL.md`) leak into single-file
304
+ // scans of configs like `.claude/settings.json`. Explicit opt-in
305
+ // via `--include-user-scope` still forces it on.
306
+ scan_user_scope: options.includeUserScope === true
307
+ ? true
308
+ : resolvedTarget.explicitCandidates && resolvedTarget.explicitCandidates.length > 0
309
+ ? false
310
+ : (baseConfig.scan_user_scope ?? false),
300
311
  };
301
312
  if (options.resetState) {
302
313
  const reset = deps.resetScanState ?? ((path) => resetScanState(path));
package/dist/scan.js CHANGED
@@ -148,18 +148,46 @@ function isPathInside(root, candidatePath) {
148
148
  * User-scope patterns (e.g. `~/.agents/skills/*/SKILL.md`) walk the whole
149
149
  * home directory, so they can match files belonging to completely unrelated
150
150
  * skills or agents. When the scan target is itself a specific location
151
- * **inside** the user's home — e.g. scanning a single skill directory any
152
- * user-scope match outside that scan target belongs to a different scan and
153
- * must not be attributed here.
151
+ * **inside** the user's home — e.g. scanning a single skill directory or a
152
+ * single config file like `~/.claude/settings.json` any user-scope match
153
+ * that does not belong to that target is a cross-scan leak and must be
154
+ * dropped.
154
155
  *
155
- * When the scan target lives outside the home directory (for example a
156
- * project root in a workspace), user-scope matches are accepted as legitimate
157
- * host-wide context for that scan.
156
+ * Three cases:
157
+ * - `scanTarget` is a directory inside `homeDir`: only keep candidates inside
158
+ * that directory (existing PR #53 behavior).
159
+ * - `scanTarget` is a file inside `homeDir`: only keep candidates that resolve
160
+ * to that exact file. "Inside" semantics do not apply to files, so the
161
+ * previous check let every sibling through.
162
+ * - `scanTarget` lives outside the home directory (or cannot be stat'd,
163
+ * e.g. a URL or a staged path that has been cleaned up): user-scope
164
+ * matches are accepted as legitimate host-wide context.
158
165
  */
159
166
  function shouldKeepUserScopeCandidate(scanTarget, homeDir, candidatePath) {
160
- if (isPathInside(homeDir, scanTarget)) {
167
+ if (!isPathInside(homeDir, scanTarget)) {
168
+ return true;
169
+ }
170
+ // Follow symlinks the same way the rest of the scan code does (walker,
171
+ // wildcard-base check, `isRegularFile`): `statSync` resolves them. If the
172
+ // target cannot be stat'd (missing / permission denied / URL that was never
173
+ // a local path), fall through to the pre-PR-#53 outside-home behavior so
174
+ // we do not over-filter project-scope scans on unusual inputs.
175
+ let targetStat;
176
+ try {
177
+ targetStat = statSync(scanTarget);
178
+ }
179
+ catch {
180
+ return true;
181
+ }
182
+ if (targetStat.isFile()) {
183
+ // Nothing is "inside" a file. The only user-scope candidate that can
184
+ // legitimately belong to a file-target scan is the file itself.
185
+ return resolve(candidatePath) === resolve(scanTarget);
186
+ }
187
+ if (targetStat.isDirectory()) {
161
188
  return isPathInside(scanTarget, candidatePath);
162
189
  }
190
+ // Sockets, devices, etc. — behave like the outside-home case.
163
191
  return true;
164
192
  }
165
193
  function toUserReportPath(pattern) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codegate-ai",
3
- "version": "0.14.2",
3
+ "version": "0.14.3",
4
4
  "description": "Pre-flight security scanner for AI coding tool configurations.",
5
5
  "license": "MIT",
6
6
  "type": "module",