surfscan 0.1.0 → 0.1.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/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.1 (2026-11-09)
4
+
5
+ - New `deps.trustedPackages` config key: DEPS-004 skips the listed packages and says which it skipped. surfscan itself is always skipped, so depending on it no longer raises DEPS-004.
6
+
3
7
  ## 0.1.0 (2026-11-05)
4
8
 
5
9
  First release.
package/README.md CHANGED
@@ -165,13 +165,14 @@ In CI, pass `--strict`. If any check could not run, surfscan exits 2 instead of
165
165
  {
166
166
  "ignore": ["RULES-006"],
167
167
  "ignorePaths": ["vendor/**"],
168
- "deps": { "minAgeDays": 30 }
168
+ "deps": { "minAgeDays": 30, "trustedPackages": ["my-new-lib"] }
169
169
  }
170
170
  ```
171
171
 
172
172
  - `ignore`: rule IDs to skip
173
173
  - `ignorePaths`: globs to exclude from scanning
174
174
  - `deps.minAgeDays`: DEPS-004 threshold in days (default 30)
175
+ - `deps.trustedPackages`: package names the DEPS-004 age check skips (surfscan itself is always skipped). Other DEPS rules still apply. A project's `surfscan.json` is controlled by whoever owns the project, so do not rely on it when scanning code you do not trust.
175
176
 
176
177
  Unknown keys are rejected with an error.
177
178
 
@@ -136,7 +136,7 @@ var CONFIG_FILE_NAME = "surfscan.json";
136
136
  var DEFAULT_CONFIG = {
137
137
  ignore: [],
138
138
  ignorePaths: [],
139
- deps: { minAgeDays: 30 }
139
+ deps: { minAgeDays: 30, trustedPackages: [] }
140
140
  };
141
141
  var ConfigError = class extends Error {
142
142
  constructor(message) {
@@ -145,7 +145,7 @@ var ConfigError = class extends Error {
145
145
  }
146
146
  };
147
147
  var TOP_LEVEL_KEYS = ["ignore", "ignorePaths", "deps"];
148
- var DEPS_KEYS = ["minAgeDays"];
148
+ var DEPS_KEYS = ["minAgeDays", "trustedPackages"];
149
149
  var RULE_ID = /^[A-Z]+-\d{3}$/;
150
150
  function isPlainObject(value) {
151
151
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -173,7 +173,7 @@ function validateConfig(raw, source = CONFIG_FILE_NAME) {
173
173
  const config = {
174
174
  ignore: [...DEFAULT_CONFIG.ignore],
175
175
  ignorePaths: [...DEFAULT_CONFIG.ignorePaths],
176
- deps: { ...DEFAULT_CONFIG.deps }
176
+ deps: { ...DEFAULT_CONFIG.deps, trustedPackages: [...DEFAULT_CONFIG.deps.trustedPackages] }
177
177
  };
178
178
  if (raw.ignore !== void 0) {
179
179
  config.ignore = stringArray(raw.ignore, "ignore", source);
@@ -198,6 +198,13 @@ function validateConfig(raw, source = CONFIG_FILE_NAME) {
198
198
  }
199
199
  config.deps.minAgeDays = minAgeDays;
200
200
  }
201
+ if (raw.deps.trustedPackages !== void 0) {
202
+ const names = stringArray(raw.deps.trustedPackages, "deps.trustedPackages", source);
203
+ if (names.some((n) => n.trim() === "")) {
204
+ throw new ConfigError(`${source}: "deps.trustedPackages" must not contain empty names`);
205
+ }
206
+ config.deps.trustedPackages = names;
207
+ }
201
208
  }
202
209
  return config;
203
210
  }
@@ -2655,6 +2662,7 @@ function dataFor(context) {
2655
2662
  function finding2(rule, ref, message, fix) {
2656
2663
  return { ruleId: rule.id, severity: rule.severity, message, file: ref.file, line: ref.line, fix };
2657
2664
  }
2665
+ var BUILT_IN_TRUSTED = ["surfscan"];
2658
2666
  async function registryFindings(context, check) {
2659
2667
  const { refs, lookups } = await dataFor(context);
2660
2668
  const out = [];
@@ -2742,19 +2750,30 @@ var rules5 = [
2742
2750
  severity: "medium",
2743
2751
  title: "Newly published package",
2744
2752
  check(context) {
2745
- const { minAgeDays } = context.config.deps;
2753
+ const { minAgeDays, trustedPackages } = context.config.deps;
2754
+ const trusted = /* @__PURE__ */ new Set([...BUILT_IN_TRUSTED, ...trustedPackages]);
2755
+ const skipped = /* @__PURE__ */ new Set();
2746
2756
  const now = Date.now();
2747
2757
  return registryFindings(context, (ref, packument) => {
2748
2758
  const created = Date.parse(packument.created ?? "");
2749
2759
  if (Number.isNaN(created)) return void 0;
2750
2760
  const ageDays = Math.floor((now - created) / DAY_MS);
2751
2761
  if (ageDays >= minAgeDays) return void 0;
2762
+ if (trusted.has(ref.name)) {
2763
+ skipped.add(ref.name);
2764
+ return void 0;
2765
+ }
2752
2766
  return finding2(
2753
2767
  this,
2754
2768
  ref,
2755
2769
  `"${ref.name}" was first published ${ageDays} day(s) ago (minimum ${minAgeDays})`,
2756
2770
  "review the package and its maintainer before using it, or wait until it has a track record"
2757
2771
  );
2772
+ }).then((findings) => {
2773
+ if (skipped.size > 0) {
2774
+ context.warn(`deps: skipped the age check (DEPS-004) for ${skipped.size} trusted package(s): ${[...skipped].join(", ")}`);
2775
+ }
2776
+ return findings;
2758
2777
  });
2759
2778
  }
2760
2779
  },
@@ -2880,4 +2899,4 @@ export {
2880
2899
  rules5,
2881
2900
  depsScanner
2882
2901
  };
2883
- //# sourceMappingURL=chunk-5SNUPVTB.js.map
2902
+ //# sourceMappingURL=chunk-MJKZGRUS.js.map