@codacy/verity-cli 0.29.2 → 0.29.4

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/bin/verity.js CHANGED
@@ -10833,6 +10833,12 @@ function writeBaselineSha(sha) {
10833
10833
  } catch {
10834
10834
  }
10835
10835
  }
10836
+ var VERITY_OWNED_PREFIXES = [".verity/", ".gate/", ".codacy/"];
10837
+ var VERITY_OWNED_FILES = ["VERITY.md"];
10838
+ function isVerityOwnedPath(file) {
10839
+ const p = file.replace(/\\/g, "/");
10840
+ return VERITY_OWNED_PREFIXES.some((d) => p.startsWith(d)) || VERITY_OWNED_FILES.includes(p);
10841
+ }
10836
10842
  function getChangedFiles() {
10837
10843
  const sets = /* @__PURE__ */ new Set();
10838
10844
  let hasRecentCommitFiles = false;
@@ -10859,18 +10865,18 @@ function getChangedFiles() {
10859
10865
  }
10860
10866
  }
10861
10867
  }
10862
- const filtered = Array.from(sets).filter((f) => !f.startsWith(".verity/") && !f.startsWith(".verity\\"));
10868
+ const filtered = Array.from(sets).filter((f) => !isVerityOwnedPath(f));
10863
10869
  return { files: filtered, hasRecentCommitFiles };
10864
10870
  }
10865
10871
  function getStagedFiles() {
10866
- return splitLines(execGit("git diff --cached --name-only")).filter((f) => !f.startsWith(".verity/") && !f.startsWith(".verity\\"));
10872
+ return splitLines(execGit("git diff --cached --name-only")).filter((f) => !isVerityOwnedPath(f));
10867
10873
  }
10868
10874
  function getDirtyFiles() {
10869
10875
  const set = /* @__PURE__ */ new Set();
10870
10876
  for (const f of splitLines(execGit("git diff --name-only HEAD"))) set.add(f);
10871
10877
  for (const f of splitLines(execGit("git diff --name-only --cached"))) set.add(f);
10872
10878
  for (const f of splitLines(execGit("git ls-files --others --exclude-standard"))) set.add(f);
10873
- return Array.from(set).filter((f) => !f.startsWith(".verity/") && !f.startsWith(".verity\\"));
10879
+ return Array.from(set).filter((f) => !isVerityOwnedPath(f));
10874
10880
  }
10875
10881
  function showContentAtRef(ref, repoRelPath) {
10876
10882
  if (!ref || ref === "no-git") return null;
@@ -16809,6 +16815,46 @@ function interpretAnalyzerRun(run) {
16809
16815
  };
16810
16816
  }
16811
16817
 
16818
+ // src/lib/remediation-guard.ts
16819
+ var TOOL_CONFIG_PATTERNS = [
16820
+ /(^|\/)\.codacy\//,
16821
+ /(^|\/)codacy\.(config\.)?(json|ya?ml)$/,
16822
+ /(^|\/)eslint\.config\.(js|mjs|cjs|ts)$/,
16823
+ /(^|\/)\.eslintrc(\.(json|ya?ml|js|cjs))?$/,
16824
+ /(^|\/)\.?semgrep(\.(ya?ml))?$/,
16825
+ /(^|\/)trivy\.ya?ml$/,
16826
+ /(^|\/)\.trivyignore$/,
16827
+ /(^|\/)sonar-project\.properties$/,
16828
+ /(^|\/)\.?ruff\.toml$/,
16829
+ /(^|\/)\.bandit$/,
16830
+ /(^|\/)pmd\.xml$/,
16831
+ /(^|\/)\.hadolint\.ya?ml$/
16832
+ ];
16833
+ function isAnalysisToolConfig(file) {
16834
+ if (!file) return false;
16835
+ const p = file.replace(/\\/g, "/");
16836
+ return TOOL_CONFIG_PATTERNS.some((re) => re.test(p));
16837
+ }
16838
+ var PROPOSES_PATTERN_ID = /["']patternId["']\s*:/;
16839
+ function screenRemediation(fix, findingFile) {
16840
+ const suggestion = typeof fix?.suggestion === "string" ? fix.suggestion : "";
16841
+ if (!suggestion) return { suggestion: null, withheldReason: null };
16842
+ const target = typeof fix?.file === "string" && fix.file || (typeof findingFile === "string" ? findingFile : "");
16843
+ if (isAnalysisToolConfig(target)) {
16844
+ return {
16845
+ suggestion: null,
16846
+ withheldReason: "withheld \u2014 Verity does not hand-write edits to analysis-tool configuration. Run `node .claude/skills/verity-setup/validate-patterns.mjs` to check this file against the real adapter catalogue."
16847
+ };
16848
+ }
16849
+ if (PROPOSES_PATTERN_ID.test(suggestion)) {
16850
+ return {
16851
+ suggestion: null,
16852
+ withheldReason: "withheld \u2014 this suggestion proposes a tool `patternId`, which Verity cannot verify. Check it with `node .claude/skills/verity-setup/validate-patterns.mjs --list <toolId>` before applying."
16853
+ };
16854
+ }
16855
+ return { suggestion, withheldReason: null };
16856
+ }
16857
+
16812
16858
  // src/lib/specs.ts
16813
16859
  var import_node_fs17 = require("node:fs");
16814
16860
  var import_node_path16 = require("node:path");
@@ -16965,7 +17011,7 @@ function resolveTaskContext(opts) {
16965
17011
  // src/lib/cli-version.ts
16966
17012
  function cliVersion() {
16967
17013
  try {
16968
- return true ? "0.29.2" : "dev";
17014
+ return true ? "0.29.4" : "dev";
16969
17015
  } catch {
16970
17016
  return "dev";
16971
17017
  }
@@ -19661,15 +19707,18 @@ ${lines.join("\n")}`,
19661
19707
  const line = f.line ?? "";
19662
19708
  const fix = f.fix;
19663
19709
  const fixDesc = fix?.description ?? "See description";
19664
- const suggestion = fix?.suggestion ?? "";
19710
+ const screened = screenRemediation(fix, f.file);
19665
19711
  process.stderr.write(`[${severity}] ${title}
19666
19712
  `);
19667
19713
  process.stderr.write(` File: ${file}:${line}
19668
19714
  `);
19669
19715
  process.stderr.write(` Fix: ${fixDesc}
19670
19716
  `);
19671
- if (suggestion) {
19672
- process.stderr.write(` Suggestion: ${suggestion}
19717
+ if (screened.suggestion) {
19718
+ process.stderr.write(` Suggestion: ${screened.suggestion}
19719
+ `);
19720
+ } else if (screened.withheldReason) {
19721
+ process.stderr.write(` Suggestion: ${screened.withheldReason}
19673
19722
  `);
19674
19723
  }
19675
19724
  process.stderr.write("\n");
@@ -21564,7 +21613,7 @@ function registerTelemetryCommands(program2) {
21564
21613
  }
21565
21614
 
21566
21615
  // src/cli.ts
21567
- program.name("verity").description("CLI for Verity quality gate service").version("0.29.2").option("--token <token>", "Override authentication token").option("--service-url <url>", "Override service URL").option("--verbose", "Log HTTP requests/responses to stderr").hook("preAction", async () => {
21616
+ program.name("verity").description("CLI for Verity quality gate service").version("0.29.4").option("--token <token>", "Override authentication token").option("--service-url <url>", "Override service URL").option("--verbose", "Log HTTP requests/responses to stderr").hook("preAction", async () => {
21568
21617
  setUserNamedServiceUrl(program.opts().serviceUrl);
21569
21618
  try {
21570
21619
  await foldLegacyLocalCredential();
@@ -221,7 +221,43 @@ it from the installed adapter. Then **always validate** (next section).
221
221
  about signal rather than a complete enumeration. Copy the pattern list for each
222
222
  tool selected for this mode. `--list <toolId> <regex>` will confirm any single ID
223
223
  or let you derive additions.
224
- 3. For tools with an **existing local config** (e.g., `eslint.config.js`): use `localConfigurationFile` to point to it. When `localConfigurationFile` is set, the tool uses its native config and the `patterns` array is ignored but you still MUST include at least one pattern in the array (the CLI crashes without it). Use a single placeholder: `[{ "patternId": "ESLint9_no-eval" }]` for ESLint, etc.
224
+ > **Semgrep ids carry a doubled suffix.** The rule name appears twiceonce as
225
+ > the path segment and once as the rule id:
226
+ > `Semgrep_javascript.browser.security.raw-html-concat.raw-html-concat`. Deriving
227
+ > them with `--list Semgrep '<regex>'` gives the correct form; constructing them by
228
+ > hand from the rule name does not.
229
+
230
+ 3. ⚠ **NEVER point `localConfigurationFile` at the project's own config** (VRT-108).
231
+ Doing so hands the tool the project's rule set, and Verity's `patterns` array
232
+ degrades to a **filter over rules someone else enabled** — it can no longer turn
233
+ a rule ON. A project that disables a rule makes it permanently unreachable by
234
+ the gate, silently. Measured: a repo whose `eslint.config.js` sets
235
+ `"@typescript-eslint/no-unused-vars": "off"` and omits `no-eval`/`eqeqeq`/`no-var`
236
+ had all four listed in its Verity config and **only** `no-explicit-any` could fire.
237
+
238
+ Setting the path is not even sufficient on its own. `useLocalConfigurationFile`
239
+ **defaults to true**, and the adapter auto-discovers `./eslint.config.js` and
240
+ overrides your value unless the flag is explicitly `false`:
241
+
242
+ ```js
243
+ // @codacy/analysis-cli — defaults to true unless EXPLICITLY false
244
+ if (!existing || existing.useLocalConfigurationFile !== false) {
245
+ config.useLocalConfigurationFile = true;
246
+ }
247
+ // patterns become the ENABLED set only when the flag is false
248
+ if (!codacyToolConfig.useLocalConfigurationFile && codacyToolConfig.patterns.length === …)
249
+ ```
250
+
251
+ So for ESLint9 always emit **both**:
252
+
253
+ - `"useLocalConfigurationFile": false` — makes `patterns` the enabled rule set
254
+ - `"localConfigurationFile": "./.codacy/tools-configs/eslint.config.mjs"` — a
255
+ **Verity-owned** config, written by this skill, so the gate does not inherit
256
+ the project's lint policy
257
+
258
+ The project's own `eslint.config.js` is left untouched and keeps serving
259
+ `npm run lint`. The two are deliberately independent: a team may reasonably turn
260
+ a rule off for their editor and still want the gate to enforce it.
225
261
  4. For tools **without** a local config: populate the full curated pattern list from `patterns-reference.yaml`
226
262
  5. **Validate** (next section). This is not optional — it is the only thing that
227
263
  catches a stale or mistyped ID, and the failure mode is silent.
@@ -245,7 +281,8 @@ Delete `.codacy/codacy.config.json` and write a new one. This example is for Typ
245
281
  "tools": [
246
282
  {
247
283
  "toolId": "ESLint9",
248
- "localConfigurationFile": "./eslint.config.js",
284
+ "localConfigurationFile": "./.codacy/tools-configs/eslint.config.mjs",
285
+ "useLocalConfigurationFile": false,
249
286
  "patterns": [
250
287
  { "patternId": "ESLint9_no-eval" },
251
288
  { "patternId": "ESLint9_no-implied-eval" },
@@ -295,6 +332,58 @@ Delete `.codacy/codacy.config.json` and write a new one. This example is for Typ
295
332
  }
296
333
  ```
297
334
 
335
+ ### Also write the Verity-owned ESLint config
336
+
337
+ Because `useLocalConfigurationFile` is `false`, the adapter builds ESLint's rule set
338
+ from the `patterns` array — but ESLint still needs a config for parser, globals and
339
+ ignores. Write `.codacy/tools-configs/eslint.config.mjs`, adapted to the project's
340
+ frameworks and ignore paths:
341
+
342
+ ```js
343
+ // Verity-owned ESLint config for the quality gate.
344
+ //
345
+ // Deliberately SEPARATE from the project's ./eslint.config.js. Pointing the gate at
346
+ // the project config makes Verity's pattern list a FILTER over rules the project
347
+ // enabled, so any rule the project disables becomes unreachable (VRT-108).
348
+ //
349
+ // Keep the rules here in sync with the ESLint9 patterns in codacy.config.json.
350
+ import js from "@eslint/js";
351
+ import globals from "globals";
352
+ import tseslint from "typescript-eslint";
353
+
354
+ export default tseslint.config(
355
+ { ignores: ["dist/**", "build/**", "node_modules/**", "coverage/**", ".codacy/**"] },
356
+ {
357
+ extends: [js.configs.recommended, ...tseslint.configs.recommended],
358
+ files: ["**/*.{ts,tsx,js,jsx}"],
359
+ languageOptions: {
360
+ ecmaVersion: 2022,
361
+ sourceType: "module",
362
+ globals: { ...globals.browser, ...globals.node },
363
+ },
364
+ rules: {
365
+ // Mirror the ESLint9 patterns in codacy.config.json.
366
+ "no-eval": "error",
367
+ "no-implied-eval": "error",
368
+ "no-new-func": "error",
369
+ "no-script-url": "error",
370
+ eqeqeq: ["error", "smart"],
371
+ "no-var": "error",
372
+ "@typescript-eslint/no-explicit-any": "warn",
373
+ "@typescript-eslint/no-unused-vars": ["warn", { argsIgnorePattern: "^_" }],
374
+ },
375
+ },
376
+ );
377
+ ```
378
+
379
+ ⚠ **Do not enable type-checked rules** (`no-unsafe-assignment`, `no-unsafe-call`,
380
+ `no-unsafe-return`). They require `parserOptions.projectService`, which makes a
381
+ per-turn gate far too slow on a large repo. Cover those through AI review in the
382
+ Standard instead.
383
+
384
+ ⚠ **Generated files belong in `ignores`** — `src/integrations/supabase/types.ts` and
385
+ similar. They are not the agent's work and will otherwise dominate the findings.
386
+
298
387
  ### Validate the config — REQUIRED, do not skip
299
388
 
300
389
  Immediately after writing `.codacy/codacy.config.json`, run the validator that ships
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codacy/verity-cli",
3
- "version": "0.29.2",
3
+ "version": "0.29.4",
4
4
  "description": "CLI for Verity quality gate service",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "homepage": "https://verity.md",