@limenet-ch/baseline 2.9.0
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/LICENSE.md +21 -0
- package/README.md +272 -0
- package/js/dist/bin.d.ts +2 -0
- package/js/dist/bin.js +59 -0
- package/js/dist/bin.js.map +1 -0
- package/js/dist/checks/allows-tooling-in-claude-settings-check.d.ts +11 -0
- package/js/dist/checks/allows-tooling-in-claude-settings-check.js +14 -0
- package/js/dist/checks/allows-tooling-in-claude-settings-check.js.map +1 -0
- package/js/dist/checks/biome-uses-local-schema-check.d.ts +21 -0
- package/js/dist/checks/biome-uses-local-schema-check.js +78 -0
- package/js/dist/checks/biome-uses-local-schema-check.js.map +1 -0
- package/js/dist/checks/calls-baseline-check.d.ts +11 -0
- package/js/dist/checks/calls-baseline-check.js +34 -0
- package/js/dist/checks/calls-baseline-check.js.map +1 -0
- package/js/dist/checks/check.d.ts +37 -0
- package/js/dist/checks/check.js +54 -0
- package/js/dist/checks/check.js.map +1 -0
- package/js/dist/checks/ci-sets-node-version-check.d.ts +10 -0
- package/js/dist/checks/ci-sets-node-version-check.js +43 -0
- package/js/dist/checks/ci-sets-node-version-check.js.map +1 -0
- package/js/dist/checks/claude-settings-check.d.ts +30 -0
- package/js/dist/checks/claude-settings-check.js +43 -0
- package/js/dist/checks/claude-settings-check.js.map +1 -0
- package/js/dist/checks/denies-env-reads-in-claude-settings-check.d.ts +13 -0
- package/js/dist/checks/denies-env-reads-in-claude-settings-check.js +25 -0
- package/js/dist/checks/denies-env-reads-in-claude-settings-check.js.map +1 -0
- package/js/dist/checks/does-not-have-copilot-or-junie-agent-files-check.d.ts +5 -0
- package/js/dist/checks/does-not-have-copilot-or-junie-agent-files-check.js +25 -0
- package/js/dist/checks/does-not-have-copilot-or-junie-agent-files-check.js.map +1 -0
- package/js/dist/checks/does-not-use-both-baseline-runners-check.d.ts +13 -0
- package/js/dist/checks/does-not-use-both-baseline-runners-check.js +30 -0
- package/js/dist/checks/does-not-use-both-baseline-runners-check.js.map +1 -0
- package/js/dist/checks/hardens-npm-supply-chain-check.d.ts +5 -0
- package/js/dist/checks/hardens-npm-supply-chain-check.js +68 -0
- package/js/dist/checks/hardens-npm-supply-chain-check.js.map +1 -0
- package/js/dist/checks/has-ci-jobs-check.d.ts +9 -0
- package/js/dist/checks/has-ci-jobs-check.js +14 -0
- package/js/dist/checks/has-ci-jobs-check.js.map +1 -0
- package/js/dist/checks/has-editorconfig-check.d.ts +6 -0
- package/js/dist/checks/has-editorconfig-check.js +42 -0
- package/js/dist/checks/has-editorconfig-check.js.map +1 -0
- package/js/dist/checks/has-npm-scripts-check.d.ts +5 -0
- package/js/dist/checks/has-npm-scripts-check.js +20 -0
- package/js/dist/checks/has-npm-scripts-check.js.map +1 -0
- package/js/dist/checks/has-trivy-config-check.d.ts +26 -0
- package/js/dist/checks/has-trivy-config-check.js +179 -0
- package/js/dist/checks/has-trivy-config-check.js.map +1 -0
- package/js/dist/checks/is-ci-lint-complete-check.d.ts +10 -0
- package/js/dist/checks/is-ci-lint-complete-check.js +25 -0
- package/js/dist/checks/is-ci-lint-complete-check.js.map +1 -0
- package/js/dist/checks/node-version-check.d.ts +5 -0
- package/js/dist/checks/node-version-check.js +77 -0
- package/js/dist/checks/node-version-check.js.map +1 -0
- package/js/dist/checks/periodic-check.d.ts +16 -0
- package/js/dist/checks/periodic-check.js +34 -0
- package/js/dist/checks/periodic-check.js.map +1 -0
- package/js/dist/checks/registry.d.ts +10 -0
- package/js/dist/checks/registry.js +56 -0
- package/js/dist/checks/registry.js.map +1 -0
- package/js/dist/checks/runs-ci-lint-hook-in-claude-settings-check.d.ts +6 -0
- package/js/dist/checks/runs-ci-lint-hook-in-claude-settings-check.js +26 -0
- package/js/dist/checks/runs-ci-lint-hook-in-claude-settings-check.js.map +1 -0
- package/js/dist/checks/updates-dependencies-check.d.ts +5 -0
- package/js/dist/checks/updates-dependencies-check.js +8 -0
- package/js/dist/checks/updates-dependencies-check.js.map +1 -0
- package/js/dist/checks/uses-release-it-check.d.ts +12 -0
- package/js/dist/checks/uses-release-it-check.js +40 -0
- package/js/dist/checks/uses-release-it-check.js.map +1 -0
- package/js/dist/commands/check.d.ts +7 -0
- package/js/dist/commands/check.js +59 -0
- package/js/dist/commands/check.js.map +1 -0
- package/js/dist/commands/install-skills.d.ts +16 -0
- package/js/dist/commands/install-skills.js +47 -0
- package/js/dist/commands/install-skills.js.map +1 -0
- package/js/dist/commands/periodic.d.ts +6 -0
- package/js/dist/commands/periodic.js +40 -0
- package/js/dist/commands/periodic.js.map +1 -0
- package/js/dist/index.d.ts +8 -0
- package/js/dist/index.js +9 -0
- package/js/dist/index.js.map +1 -0
- package/js/dist/policy.d.ts +45 -0
- package/js/dist/policy.js +145 -0
- package/js/dist/policy.js.map +1 -0
- package/js/dist/project.d.ts +26 -0
- package/js/dist/project.js +64 -0
- package/js/dist/project.js.map +1 -0
- package/js/dist/state.d.ts +21 -0
- package/js/dist/state.js +36 -0
- package/js/dist/state.js.map +1 -0
- package/js/dist/support/ci-jobs.d.ts +13 -0
- package/js/dist/support/ci-jobs.js +42 -0
- package/js/dist/support/ci-jobs.js.map +1 -0
- package/js/dist/support/npmrc.d.ts +10 -0
- package/js/dist/support/npmrc.js +36 -0
- package/js/dist/support/npmrc.js.map +1 -0
- package/js/dist/support/versions.d.ts +10 -0
- package/js/dist/support/versions.js +34 -0
- package/js/dist/support/versions.js.map +1 -0
- package/js/skills/creating-a-release/SKILL.md +45 -0
- package/js/skills/updating-dependencies/SKILL.md +104 -0
- package/package.json +67 -0
- package/policy/policy.json +131 -0
- package/policy/policy.schema.json +284 -0
- package/policy/templates/editorconfig +18 -0
- package/policy/templates/trivy.yaml +19 -0
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
export function isError(result) {
|
|
2
|
+
return result === 'fail';
|
|
3
|
+
}
|
|
4
|
+
export function icon(result) {
|
|
5
|
+
return { pass: '✅', fail: '❌', warn: '⚠️' }[result];
|
|
6
|
+
}
|
|
7
|
+
/** Collects the actionable messages a single check run produced. */
|
|
8
|
+
export class CommentCollector {
|
|
9
|
+
comments = [];
|
|
10
|
+
add(comment) {
|
|
11
|
+
this.comments.push(comment);
|
|
12
|
+
}
|
|
13
|
+
all() {
|
|
14
|
+
return [...this.comments];
|
|
15
|
+
}
|
|
16
|
+
reset() {
|
|
17
|
+
this.comments = [];
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
export class Check {
|
|
21
|
+
project;
|
|
22
|
+
comments;
|
|
23
|
+
/**
|
|
24
|
+
* The check's stable identifier — the same string the PHP runner derives from
|
|
25
|
+
* its class name, and the key used in .baseline.json excludes and in fixtures.
|
|
26
|
+
* Declared explicitly rather than derived, so a bundler renaming a class can
|
|
27
|
+
* never silently rename a check.
|
|
28
|
+
*/
|
|
29
|
+
static checkName;
|
|
30
|
+
constructor(project, comments) {
|
|
31
|
+
this.project = project;
|
|
32
|
+
this.comments = comments;
|
|
33
|
+
}
|
|
34
|
+
comment(message) {
|
|
35
|
+
this.comments.add(message);
|
|
36
|
+
}
|
|
37
|
+
get name() {
|
|
38
|
+
return this.constructor.checkName;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* A check that can repair what it detects. check() is the dry run, so detection
|
|
43
|
+
* and repair can never drift apart — the same shape as the PHP
|
|
44
|
+
* AbstractFixableCheck.
|
|
45
|
+
*/
|
|
46
|
+
export class FixableCheck extends Check {
|
|
47
|
+
check() {
|
|
48
|
+
return this.fix(true);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
export function isFixable(check) {
|
|
52
|
+
return check instanceof FixableCheck;
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=check.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"check.js","sourceRoot":"","sources":["../../src/checks/check.ts"],"names":[],"mappings":"AAKA,MAAM,UAAU,OAAO,CAAC,MAAmB;IACvC,OAAO,MAAM,KAAK,MAAM,CAAA;AAC5B,CAAC;AAED,MAAM,UAAU,IAAI,CAAC,MAAmB;IACpC,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,MAAM,CAAC,CAAA;AACvD,CAAC;AAED,oEAAoE;AACpE,MAAM,OAAO,gBAAgB;IACjB,QAAQ,GAAa,EAAE,CAAA;IAE/B,GAAG,CAAC,OAAe;QACf,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;IAC/B,CAAC;IAED,GAAG;QACC,OAAO,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAA;IAC7B,CAAC;IAED,KAAK;QACD,IAAI,CAAC,QAAQ,GAAG,EAAE,CAAA;IACtB,CAAC;CACJ;AAED,MAAM,OAAgB,KAAK;IAUA,OAAO;IACP,QAAQ;IAV/B;;;;;OAKG;IACH,MAAM,CAAU,SAAS,CAAQ;IAEjC,YACuB,OAAgB,EAChB,QAA0B;uBAD1B,OAAO;wBACP,QAAQ;IAC5B,CAAC;IAIM,OAAO,CAAC,OAAe;QAC7B,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;IAC9B,CAAC;IAED,IAAI,IAAI;QACJ,OAAQ,IAAI,CAAC,WAA4B,CAAC,SAAS,CAAA;IACvD,CAAC;CACJ;AAED;;;;GAIG;AACH,MAAM,OAAgB,YAAa,SAAQ,KAAK;IAC5C,KAAK;QACD,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;IACzB,CAAC;CAGJ;AAED,MAAM,UAAU,SAAS,CAAC,KAAY;IAClC,OAAO,KAAK,YAAY,YAAY,CAAA;AACxC,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type CheckResult, FixableCheck } from './check.js';
|
|
2
|
+
/**
|
|
3
|
+
* With no DDEV to derive the Node version from the project, the CI variable is
|
|
4
|
+
* what keeps the pipeline and the developer's machine on the same major — the
|
|
5
|
+
* shared template resolves it.
|
|
6
|
+
*/
|
|
7
|
+
export declare class CiSetsNodeVersionCheck extends FixableCheck {
|
|
8
|
+
static readonly checkName = "ciSetsNodeVersion";
|
|
9
|
+
fix(dry?: boolean): CheckResult;
|
|
10
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { parseDocument } from 'yaml';
|
|
2
|
+
import { policy } from '../policy.js';
|
|
3
|
+
import { FixableCheck } from './check.js';
|
|
4
|
+
const CI_FILE = '.gitlab-ci.yml';
|
|
5
|
+
/**
|
|
6
|
+
* With no DDEV to derive the Node version from the project, the CI variable is
|
|
7
|
+
* what keeps the pipeline and the developer's machine on the same major — the
|
|
8
|
+
* shared template resolves it.
|
|
9
|
+
*/
|
|
10
|
+
export class CiSetsNodeVersionCheck extends FixableCheck {
|
|
11
|
+
static checkName = 'ciSetsNodeVersion';
|
|
12
|
+
fix(dry = false) {
|
|
13
|
+
const contents = this.project.read(CI_FILE);
|
|
14
|
+
if (contents === null) {
|
|
15
|
+
this.comment(`${CI_FILE} not found`);
|
|
16
|
+
return 'fail';
|
|
17
|
+
}
|
|
18
|
+
const name = policy().string('ci.nodeVersionVariable.name');
|
|
19
|
+
const value = policy().string('ci.nodeVersionVariable.value');
|
|
20
|
+
// parseDocument() collects syntax errors on the document instead of
|
|
21
|
+
// throwing, and such a document cannot be stringified — so this has to be
|
|
22
|
+
// checked up front, or the write below crashes the CLI on a malformed
|
|
23
|
+
// pipeline rather than reporting it.
|
|
24
|
+
const document = parseDocument(contents);
|
|
25
|
+
if (document.errors.length > 0) {
|
|
26
|
+
this.comment(`${CI_FILE} could not be parsed: ${document.errors[0]?.message ?? 'invalid YAML'}`);
|
|
27
|
+
return 'fail';
|
|
28
|
+
}
|
|
29
|
+
if (String(document.getIn(['variables', name]) ?? '') === value) {
|
|
30
|
+
return 'pass';
|
|
31
|
+
}
|
|
32
|
+
this.comment(`${CI_FILE} must set variables.${name} to "${value}" so the shared CI template resolves the Node version`);
|
|
33
|
+
if (dry) {
|
|
34
|
+
return 'fail';
|
|
35
|
+
}
|
|
36
|
+
// setIn on the parsed document rather than a re-dump, so comments and
|
|
37
|
+
// formatting elsewhere in the pipeline survive.
|
|
38
|
+
document.setIn(['variables', name], value);
|
|
39
|
+
this.project.write(CI_FILE, document.toString());
|
|
40
|
+
return this.fix(true);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=ci-sets-node-version-check.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ci-sets-node-version-check.js","sourceRoot":"","sources":["../../src/checks/ci-sets-node-version-check.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,MAAM,CAAA;AACpC,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AACrC,OAAO,EAAoB,YAAY,EAAE,MAAM,YAAY,CAAA;AAE3D,MAAM,OAAO,GAAG,gBAAgB,CAAA;AAEhC;;;;GAIG;AACH,MAAM,OAAO,sBAAuB,SAAQ,YAAY;IACpD,MAAM,CAAmB,SAAS,GAAG,mBAAmB,CAAA;IAExD,GAAG,CAAC,GAAG,GAAG,KAAK;QACX,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;QAE3C,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;YACpB,IAAI,CAAC,OAAO,CAAC,GAAG,OAAO,YAAY,CAAC,CAAA;YAEpC,OAAO,MAAM,CAAA;QACjB,CAAC;QAED,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,MAAM,CAAC,6BAA6B,CAAC,CAAA;QAC3D,MAAM,KAAK,GAAG,MAAM,EAAE,CAAC,MAAM,CAAC,8BAA8B,CAAC,CAAA;QAE7D,oEAAoE;QACpE,0EAA0E;QAC1E,sEAAsE;QACtE,qCAAqC;QACrC,MAAM,QAAQ,GAAG,aAAa,CAAC,QAAQ,CAAC,CAAA;QAExC,IAAI,QAAQ,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC7B,IAAI,CAAC,OAAO,CAAC,GAAG,OAAO,yBAAyB,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,OAAO,IAAI,cAAc,EAAE,CAAC,CAAA;YAEhG,OAAO,MAAM,CAAA;QACjB,CAAC;QAED,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC,KAAK,KAAK,EAAE,CAAC;YAC9D,OAAO,MAAM,CAAA;QACjB,CAAC;QAED,IAAI,CAAC,OAAO,CACR,GAAG,OAAO,uBAAuB,IAAI,QAAQ,KAAK,uDAAuD,CAC5G,CAAA;QAED,IAAI,GAAG,EAAE,CAAC;YACN,OAAO,MAAM,CAAA;QACjB,CAAC;QAED,sEAAsE;QACtE,gDAAgD;QAChD,QAAQ,CAAC,KAAK,CAAC,CAAC,WAAW,EAAE,IAAI,CAAC,EAAE,KAAK,CAAC,CAAA;QAC1C,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO,EAAE,QAAQ,CAAC,QAAQ,EAAE,CAAC,CAAA;QAEhD,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;IACzB,CAAC;CACJ"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { type CheckResult, FixableCheck } from './check.js';
|
|
2
|
+
export declare const CLAUDE_SETTINGS_FILE = ".claude/settings.json";
|
|
3
|
+
export interface ClaudeSettings {
|
|
4
|
+
permissions?: {
|
|
5
|
+
allow?: string[];
|
|
6
|
+
deny?: string[];
|
|
7
|
+
};
|
|
8
|
+
hooks?: Record<string, Array<{
|
|
9
|
+
matcher?: string;
|
|
10
|
+
hooks?: Array<{
|
|
11
|
+
type?: string;
|
|
12
|
+
command?: string;
|
|
13
|
+
}>;
|
|
14
|
+
}>>;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Shared plumbing for the .claude/settings.json checks, mirroring
|
|
18
|
+
* src/Checks/AbstractClaudeSettingsCheck.php.
|
|
19
|
+
*/
|
|
20
|
+
export declare abstract class ClaudeSettingsCheck extends FixableCheck {
|
|
21
|
+
protected readSettings(): ClaudeSettings;
|
|
22
|
+
protected writeSettings(settings: ClaudeSettings): void;
|
|
23
|
+
/** Append the required values that are absent, preserving existing entries and order. */
|
|
24
|
+
protected mergeMissing(existing: string[], required: string[]): string[];
|
|
25
|
+
/**
|
|
26
|
+
* The permissions.allow / permissions.deny checks are the same shape: diff a
|
|
27
|
+
* required list against what is there, comment on each gap, then append.
|
|
28
|
+
*/
|
|
29
|
+
protected requireEntries(kind: 'allow' | 'deny', required: string[], message: (entry: string) => string, dry: boolean): CheckResult;
|
|
30
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { FixableCheck } from './check.js';
|
|
2
|
+
export const CLAUDE_SETTINGS_FILE = '.claude/settings.json';
|
|
3
|
+
/**
|
|
4
|
+
* Shared plumbing for the .claude/settings.json checks, mirroring
|
|
5
|
+
* src/Checks/AbstractClaudeSettingsCheck.php.
|
|
6
|
+
*/
|
|
7
|
+
export class ClaudeSettingsCheck extends FixableCheck {
|
|
8
|
+
readSettings() {
|
|
9
|
+
return this.project.readJson(CLAUDE_SETTINGS_FILE) ?? {};
|
|
10
|
+
}
|
|
11
|
+
writeSettings(settings) {
|
|
12
|
+
this.project.write(CLAUDE_SETTINGS_FILE, `${JSON.stringify(settings, null, 4)}\n`);
|
|
13
|
+
}
|
|
14
|
+
/** Append the required values that are absent, preserving existing entries and order. */
|
|
15
|
+
mergeMissing(existing, required) {
|
|
16
|
+
return [...existing, ...required.filter((value) => !existing.includes(value))];
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* The permissions.allow / permissions.deny checks are the same shape: diff a
|
|
20
|
+
* required list against what is there, comment on each gap, then append.
|
|
21
|
+
*/
|
|
22
|
+
requireEntries(kind, required, message, dry) {
|
|
23
|
+
const settings = this.readSettings();
|
|
24
|
+
const existing = settings.permissions?.[kind] ?? [];
|
|
25
|
+
const missing = required.filter((entry) => !existing.includes(entry));
|
|
26
|
+
if (missing.length === 0) {
|
|
27
|
+
return 'pass';
|
|
28
|
+
}
|
|
29
|
+
for (const entry of missing) {
|
|
30
|
+
this.comment(message(entry));
|
|
31
|
+
}
|
|
32
|
+
if (dry) {
|
|
33
|
+
return 'fail';
|
|
34
|
+
}
|
|
35
|
+
settings.permissions = {
|
|
36
|
+
...settings.permissions,
|
|
37
|
+
[kind]: this.mergeMissing(existing, required),
|
|
38
|
+
};
|
|
39
|
+
this.writeSettings(settings);
|
|
40
|
+
return 'pass';
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=claude-settings-check.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude-settings-check.js","sourceRoot":"","sources":["../../src/checks/claude-settings-check.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoB,YAAY,EAAE,MAAM,YAAY,CAAA;AAE3D,MAAM,CAAC,MAAM,oBAAoB,GAAG,uBAAuB,CAAA;AAO3D;;;GAGG;AACH,MAAM,OAAgB,mBAAoB,SAAQ,YAAY;IAChD,YAAY;QAClB,OAAO,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAiB,oBAAoB,CAAC,IAAI,EAAE,CAAA;IAC5E,CAAC;IAES,aAAa,CAAC,QAAwB;QAC5C,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,oBAAoB,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAA;IACtF,CAAC;IAED,yFAAyF;IAC/E,YAAY,CAAC,QAAkB,EAAE,QAAkB;QACzD,OAAO,CAAC,GAAG,QAAQ,EAAE,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;IAClF,CAAC;IAED;;;OAGG;IACO,cAAc,CACpB,IAAsB,EACtB,QAAkB,EAClB,OAAkC,EAClC,GAAY;QAEZ,MAAM,QAAQ,GAAG,IAAI,CAAC,YAAY,EAAE,CAAA;QACpC,MAAM,QAAQ,GAAG,QAAQ,CAAC,WAAW,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAA;QACnD,MAAM,OAAO,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAA;QAErE,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACvB,OAAO,MAAM,CAAA;QACjB,CAAC;QAED,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YAC1B,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAA;QAChC,CAAC;QAED,IAAI,GAAG,EAAE,CAAC;YACN,OAAO,MAAM,CAAA;QACjB,CAAC;QAED,QAAQ,CAAC,WAAW,GAAG;YACnB,GAAG,QAAQ,CAAC,WAAW;YACvB,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,YAAY,CAAC,QAAQ,EAAE,QAAQ,CAAC;SAChD,CAAA;QAED,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAA;QAE5B,OAAO,MAAM,CAAA;IACjB,CAAC;CACJ"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { CheckResult } from './check.js';
|
|
2
|
+
import { ClaudeSettingsCheck } from './claude-settings-check.js';
|
|
3
|
+
export declare class DeniesEnvReadsInClaudeSettingsCheck extends ClaudeSettingsCheck {
|
|
4
|
+
static readonly checkName = "deniesEnvReadsInClaudeSettings";
|
|
5
|
+
fix(dry?: boolean): CheckResult;
|
|
6
|
+
/**
|
|
7
|
+
* The policy's shared entries plus every environment that ships an encrypted
|
|
8
|
+
* file (.env.{environment}.encrypted => deny .env.{environment}).
|
|
9
|
+
*
|
|
10
|
+
* .env.example is intentionally never denied so it stays readable.
|
|
11
|
+
*/
|
|
12
|
+
private requiredDenyEntries;
|
|
13
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { readdirSync } from 'node:fs';
|
|
2
|
+
import { policy } from '../policy.js';
|
|
3
|
+
import { ClaudeSettingsCheck } from './claude-settings-check.js';
|
|
4
|
+
export class DeniesEnvReadsInClaudeSettingsCheck extends ClaudeSettingsCheck {
|
|
5
|
+
static checkName = 'deniesEnvReadsInClaudeSettings';
|
|
6
|
+
fix(dry = false) {
|
|
7
|
+
return this.requireEntries('deny', this.requiredDenyEntries(), (entry) => `Claude settings: add "${entry}" to permissions.deny in .claude/settings.json — prevents Claude from reading secrets`, dry);
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* The policy's shared entries plus every environment that ships an encrypted
|
|
11
|
+
* file (.env.{environment}.encrypted => deny .env.{environment}).
|
|
12
|
+
*
|
|
13
|
+
* .env.example is intentionally never denied so it stays readable.
|
|
14
|
+
*/
|
|
15
|
+
requiredDenyEntries() {
|
|
16
|
+
const entries = policy().strings('claude.deny.shared');
|
|
17
|
+
for (const name of readdirSync(this.project.basePath).sort()) {
|
|
18
|
+
if (name.startsWith('.env.') && name.endsWith('.encrypted')) {
|
|
19
|
+
entries.push(`Read(./${name.slice(0, -'.encrypted'.length)})`);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return entries;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=denies-env-reads-in-claude-settings-check.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"denies-env-reads-in-claude-settings-check.js","sourceRoot":"","sources":["../../src/checks/denies-env-reads-in-claude-settings-check.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AACrC,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AAErC,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAA;AAEhE,MAAM,OAAO,mCAAoC,SAAQ,mBAAmB;IACxE,MAAM,CAAmB,SAAS,GAAG,gCAAgC,CAAA;IAErE,GAAG,CAAC,GAAG,GAAG,KAAK;QACX,OAAO,IAAI,CAAC,cAAc,CACtB,MAAM,EACN,IAAI,CAAC,mBAAmB,EAAE,EAC1B,CAAC,KAAK,EAAE,EAAE,CACN,yBAAyB,KAAK,uFAAuF,EACzH,GAAG,CACN,CAAA;IACL,CAAC;IAED;;;;;OAKG;IACK,mBAAmB;QACvB,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,oBAAoB,CAAC,CAAA;QAEtD,KAAK,MAAM,IAAI,IAAI,WAAW,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;YAC3D,IAAI,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC;gBAC1D,OAAO,CAAC,IAAI,CAAC,UAAU,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,YAAY,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;YAClE,CAAC;QACL,CAAC;QAED,OAAO,OAAO,CAAA;IAClB,CAAC;CACJ"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { policy } from '../policy.js';
|
|
2
|
+
import { FixableCheck } from './check.js';
|
|
3
|
+
export class DoesNotHaveCopilotOrJunieAgentFilesCheck extends FixableCheck {
|
|
4
|
+
static checkName = 'doesNotHaveCopilotOrJunieAgentFiles';
|
|
5
|
+
fix(dry = false) {
|
|
6
|
+
const present = [];
|
|
7
|
+
for (const [path, reason] of Object.entries(policy().stringMap('agentFiles.forbidden'))) {
|
|
8
|
+
if (this.project.exists(path)) {
|
|
9
|
+
present.push(path);
|
|
10
|
+
this.comment(`Remove ${path} — ${reason}`);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
if (present.length === 0) {
|
|
14
|
+
return 'pass';
|
|
15
|
+
}
|
|
16
|
+
if (dry) {
|
|
17
|
+
return 'fail';
|
|
18
|
+
}
|
|
19
|
+
for (const path of present) {
|
|
20
|
+
this.project.remove(path);
|
|
21
|
+
}
|
|
22
|
+
return this.fix(true);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=does-not-have-copilot-or-junie-agent-files-check.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"does-not-have-copilot-or-junie-agent-files-check.js","sourceRoot":"","sources":["../../src/checks/does-not-have-copilot-or-junie-agent-files-check.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AACrC,OAAO,EAAoB,YAAY,EAAE,MAAM,YAAY,CAAA;AAE3D,MAAM,OAAO,wCAAyC,SAAQ,YAAY;IACtE,MAAM,CAAmB,SAAS,GAAG,qCAAqC,CAAA;IAE1E,GAAG,CAAC,GAAG,GAAG,KAAK;QACX,MAAM,OAAO,GAAa,EAAE,CAAA;QAE5B,KAAK,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,SAAS,CAAC,sBAAsB,CAAC,CAAC,EAAE,CAAC;YACtF,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC5B,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;gBAClB,IAAI,CAAC,OAAO,CAAC,UAAU,IAAI,MAAM,MAAM,EAAE,CAAC,CAAA;YAC9C,CAAC;QACL,CAAC;QAED,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACvB,OAAO,MAAM,CAAA;QACjB,CAAC;QAED,IAAI,GAAG,EAAE,CAAC;YACN,OAAO,MAAM,CAAA;QACjB,CAAC;QAED,KAAK,MAAM,IAAI,IAAI,OAAO,EAAE,CAAC;YACzB,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;QAC7B,CAAC;QAED,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;IACzB,CAAC;CACJ"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Check, type CheckResult } from './check.js';
|
|
2
|
+
/**
|
|
3
|
+
* The mirror image of the PHP runner's check of the same name: a project runs
|
|
4
|
+
* one runner, and where both could run, the Composer one wins. It is the larger
|
|
5
|
+
* registry — every check here has a counterpart there, plus the Laravel ones —
|
|
6
|
+
* so this runner is the one that steps aside.
|
|
7
|
+
*
|
|
8
|
+
* Uninstalling is the developer's call, so this detects and reports.
|
|
9
|
+
*/
|
|
10
|
+
export declare class DoesNotUseBothBaselineRunnersCheck extends Check {
|
|
11
|
+
static readonly checkName = "doesNotUseBothBaselineRunners";
|
|
12
|
+
check(): CheckResult;
|
|
13
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { policy } from '../policy.js';
|
|
2
|
+
import { Check } from './check.js';
|
|
3
|
+
/**
|
|
4
|
+
* The mirror image of the PHP runner's check of the same name: a project runs
|
|
5
|
+
* one runner, and where both could run, the Composer one wins. It is the larger
|
|
6
|
+
* registry — every check here has a counterpart there, plus the Laravel ones —
|
|
7
|
+
* so this runner is the one that steps aside.
|
|
8
|
+
*
|
|
9
|
+
* Uninstalling is the developer's call, so this detects and reports.
|
|
10
|
+
*/
|
|
11
|
+
export class DoesNotUseBothBaselineRunnersCheck extends Check {
|
|
12
|
+
static checkName = 'doesNotUseBothBaselineRunners';
|
|
13
|
+
check() {
|
|
14
|
+
const composerJson = this.project.readJson('composer.json');
|
|
15
|
+
if (composerJson === null) {
|
|
16
|
+
return 'pass';
|
|
17
|
+
}
|
|
18
|
+
const composerPackage = policy().string('baseline.composerPackage');
|
|
19
|
+
for (const section of ['require', 'require-dev']) {
|
|
20
|
+
if (composerJson[section]?.[composerPackage] === undefined) {
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
23
|
+
const npmPackage = policy().string('baseline.npmPackage');
|
|
24
|
+
this.comment(`Two baseline runners in one project: composer.json requires ${composerPackage} in ${section}, and that runner covers everything this one does and more. Run "npm uninstall ${npmPackage}" and drop its "baseline check" entry from the ci-lint npm script.`);
|
|
25
|
+
return 'fail';
|
|
26
|
+
}
|
|
27
|
+
return 'pass';
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=does-not-use-both-baseline-runners-check.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"does-not-use-both-baseline-runners-check.js","sourceRoot":"","sources":["../../src/checks/does-not-use-both-baseline-runners-check.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AACrC,OAAO,EAAE,KAAK,EAAoB,MAAM,YAAY,CAAA;AAOpD;;;;;;;GAOG;AACH,MAAM,OAAO,kCAAmC,SAAQ,KAAK;IACzD,MAAM,CAAmB,SAAS,GAAG,+BAA+B,CAAA;IAEpE,KAAK;QACD,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAe,eAAe,CAAC,CAAA;QAEzE,IAAI,YAAY,KAAK,IAAI,EAAE,CAAC;YACxB,OAAO,MAAM,CAAA;QACjB,CAAC;QAED,MAAM,eAAe,GAAG,MAAM,EAAE,CAAC,MAAM,CAAC,0BAA0B,CAAC,CAAA;QAEnE,KAAK,MAAM,OAAO,IAAI,CAAC,SAAS,EAAE,aAAa,CAAU,EAAE,CAAC;YACxD,IAAI,YAAY,CAAC,OAAO,CAAC,EAAE,CAAC,eAAe,CAAC,KAAK,SAAS,EAAE,CAAC;gBACzD,SAAQ;YACZ,CAAC;YAED,MAAM,UAAU,GAAG,MAAM,EAAE,CAAC,MAAM,CAAC,qBAAqB,CAAC,CAAA;YAEzD,IAAI,CAAC,OAAO,CACR,+DAA+D,eAAe,OAAO,OAAO,kFAAkF,UAAU,oEAAoE,CAC/P,CAAA;YAED,OAAO,MAAM,CAAA;QACjB,CAAC;QAED,OAAO,MAAM,CAAA;IACjB,CAAC;CACJ"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { policy } from '../policy.js';
|
|
2
|
+
import { parseNpmrc, upsertNpmrc } from '../support/npmrc.js';
|
|
3
|
+
import { guaranteesAtLeast } from '../support/versions.js';
|
|
4
|
+
import { FixableCheck } from './check.js';
|
|
5
|
+
export class HardensNpmSupplyChainCheck extends FixableCheck {
|
|
6
|
+
static checkName = 'hardensNpmSupplyChain';
|
|
7
|
+
fix(dry = false) {
|
|
8
|
+
const packageJson = this.project.readJson('package.json');
|
|
9
|
+
if (packageJson === null) {
|
|
10
|
+
this.comment('Package.json missing: Create package.json in project root');
|
|
11
|
+
return 'fail';
|
|
12
|
+
}
|
|
13
|
+
// npm 12 blocks dependency lifecycle scripts by default and defaults
|
|
14
|
+
// --allow-git/--allow-remote to none, so requiring it hardens installs for free.
|
|
15
|
+
const floor = policy().string('npm.minMajor');
|
|
16
|
+
const constraint = policy().string('npm.constraint');
|
|
17
|
+
const minReleaseAgeDays = policy().number('npm.minReleaseAgeDays');
|
|
18
|
+
const npm = packageJson.engines?.npm ?? null;
|
|
19
|
+
const npmTooLow = npm !== null && !guaranteesAtLeast(npm, floor);
|
|
20
|
+
if (npm === null) {
|
|
21
|
+
this.comment(`package.json missing engines.npm: add "engines": { "npm": "${constraint}" } (npm ${floor} blocks dependency lifecycle scripts by default)`);
|
|
22
|
+
if (dry) {
|
|
23
|
+
return 'fail';
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
else if (npmTooLow) {
|
|
27
|
+
this.comment(`engines.npm (${npm}) allows npm < ${floor}; require npm >= ${floor} (e.g. "${constraint}")`);
|
|
28
|
+
if (dry) {
|
|
29
|
+
return 'fail';
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
const npmrcContents = this.project.read('.npmrc');
|
|
33
|
+
const npmrc = parseNpmrc(npmrcContents);
|
|
34
|
+
if (policy().boolean('npm.engineStrict') && npmrc['engine-strict'] !== 'true') {
|
|
35
|
+
this.comment('.npmrc missing engine-strict=true: add "engine-strict=true" so the required npm version is enforced, not just advised');
|
|
36
|
+
if (dry) {
|
|
37
|
+
return 'fail';
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
const declared = npmrc['min-release-age'];
|
|
41
|
+
const minReleaseAge = declared === undefined || declared === '' ? null : Number.parseInt(declared, 10);
|
|
42
|
+
if (minReleaseAge === null || Number.isNaN(minReleaseAge)) {
|
|
43
|
+
this.comment(`.npmrc missing min-release-age: add "min-release-age=${minReleaseAgeDays}" for a ${minReleaseAgeDays}-day dependency install cooldown`);
|
|
44
|
+
if (dry) {
|
|
45
|
+
return 'fail';
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
else if (minReleaseAge < minReleaseAgeDays) {
|
|
49
|
+
this.comment(`.npmrc min-release-age (${minReleaseAge}) is below the recommended ${minReleaseAgeDays}-day cooldown`);
|
|
50
|
+
if (dry) {
|
|
51
|
+
return 'fail';
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
if (dry) {
|
|
55
|
+
return 'pass';
|
|
56
|
+
}
|
|
57
|
+
if (npm === null || npmTooLow) {
|
|
58
|
+
packageJson.engines = { ...packageJson.engines, npm: constraint };
|
|
59
|
+
this.project.writeJson('package.json', packageJson);
|
|
60
|
+
}
|
|
61
|
+
this.project.write('.npmrc', upsertNpmrc(npmrcContents, {
|
|
62
|
+
'engine-strict': 'true',
|
|
63
|
+
'min-release-age': String(minReleaseAgeDays),
|
|
64
|
+
}));
|
|
65
|
+
return this.fix(true);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=hardens-npm-supply-chain-check.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hardens-npm-supply-chain-check.js","sourceRoot":"","sources":["../../src/checks/hardens-npm-supply-chain-check.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Check, type CheckResult } from './check.js';
|
|
2
|
+
/**
|
|
3
|
+
* The same shared GitLab CI templates the Laravel projects extend, minus the
|
|
4
|
+
* php job — see ci.requiredJobs.js in the policy.
|
|
5
|
+
*/
|
|
6
|
+
export declare class HasCiJobsCheck extends Check {
|
|
7
|
+
static readonly checkName = "hasCiJobs";
|
|
8
|
+
check(): CheckResult;
|
|
9
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { policy } from '../policy.js';
|
|
2
|
+
import { checkRequiredCiJobs } from '../support/ci-jobs.js';
|
|
3
|
+
import { Check } from './check.js';
|
|
4
|
+
/**
|
|
5
|
+
* The same shared GitLab CI templates the Laravel projects extend, minus the
|
|
6
|
+
* php job — see ci.requiredJobs.js in the policy.
|
|
7
|
+
*/
|
|
8
|
+
export class HasCiJobsCheck extends Check {
|
|
9
|
+
static checkName = 'hasCiJobs';
|
|
10
|
+
check() {
|
|
11
|
+
return checkRequiredCiJobs(this.project, policy().stringListMap('ci.requiredJobs.js'), (message) => this.comment(message));
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=has-ci-jobs-check.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"has-ci-jobs-check.js","sourceRoot":"","sources":["../../src/checks/has-ci-jobs-check.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AACrC,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAA;AAC3D,OAAO,EAAE,KAAK,EAAoB,MAAM,YAAY,CAAA;AAEpD;;;GAGG;AACH,MAAM,OAAO,cAAe,SAAQ,KAAK;IACrC,MAAM,CAAmB,SAAS,GAAG,WAAW,CAAA;IAEhD,KAAK;QACD,OAAO,mBAAmB,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC,aAAa,CAAC,oBAAoB,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,CAC/F,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CACxB,CAAA;IACL,CAAC;CACJ"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { policy } from '../policy.js';
|
|
2
|
+
import { FixableCheck } from './check.js';
|
|
3
|
+
export class HasEditorconfigCheck extends FixableCheck {
|
|
4
|
+
static checkName = 'hasEditorconfig';
|
|
5
|
+
fix(dry = false) {
|
|
6
|
+
const contents = this.project.read('.editorconfig');
|
|
7
|
+
if (contents === null) {
|
|
8
|
+
this.comment('Editorconfig missing: Create .editorconfig in project root');
|
|
9
|
+
if (dry) {
|
|
10
|
+
return 'fail';
|
|
11
|
+
}
|
|
12
|
+
this.project.write('.editorconfig', this.canonicalContent());
|
|
13
|
+
return this.fix(true);
|
|
14
|
+
}
|
|
15
|
+
if (contents.trim() === '') {
|
|
16
|
+
this.comment('Editorconfig empty: Add content to .editorconfig');
|
|
17
|
+
if (dry) {
|
|
18
|
+
return 'fail';
|
|
19
|
+
}
|
|
20
|
+
this.project.write('.editorconfig', this.canonicalContent());
|
|
21
|
+
return this.fix(true);
|
|
22
|
+
}
|
|
23
|
+
// A subset of the canonical file, so a project may add its own sections
|
|
24
|
+
// without failing.
|
|
25
|
+
const missing = policy()
|
|
26
|
+
.strings('editorconfig.requiredProperties')
|
|
27
|
+
.filter((property) => !contents.includes(property));
|
|
28
|
+
if (missing.length === 0) {
|
|
29
|
+
return 'pass';
|
|
30
|
+
}
|
|
31
|
+
this.comment(`Editorconfig incomplete: Add "${missing[0]}" to .editorconfig`);
|
|
32
|
+
if (dry) {
|
|
33
|
+
return 'fail';
|
|
34
|
+
}
|
|
35
|
+
this.project.write('.editorconfig', this.canonicalContent());
|
|
36
|
+
return this.fix(true);
|
|
37
|
+
}
|
|
38
|
+
canonicalContent() {
|
|
39
|
+
return policy().template(policy().string('editorconfig.template'));
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=has-editorconfig-check.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"has-editorconfig-check.js","sourceRoot":"","sources":["../../src/checks/has-editorconfig-check.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AACrC,OAAO,EAAoB,YAAY,EAAE,MAAM,YAAY,CAAA;AAE3D,MAAM,OAAO,oBAAqB,SAAQ,YAAY;IAClD,MAAM,CAAmB,SAAS,GAAG,iBAAiB,CAAA;IAEtD,GAAG,CAAC,GAAG,GAAG,KAAK;QACX,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,eAAe,CAAC,CAAA;QAEnD,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;YACpB,IAAI,CAAC,OAAO,CAAC,4DAA4D,CAAC,CAAA;YAE1E,IAAI,GAAG,EAAE,CAAC;gBACN,OAAO,MAAM,CAAA;YACjB,CAAC;YAED,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,eAAe,EAAE,IAAI,CAAC,gBAAgB,EAAE,CAAC,CAAA;YAE5D,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;QACzB,CAAC;QAED,IAAI,QAAQ,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;YACzB,IAAI,CAAC,OAAO,CAAC,kDAAkD,CAAC,CAAA;YAEhE,IAAI,GAAG,EAAE,CAAC;gBACN,OAAO,MAAM,CAAA;YACjB,CAAC;YAED,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,eAAe,EAAE,IAAI,CAAC,gBAAgB,EAAE,CAAC,CAAA;YAE5D,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;QACzB,CAAC;QAED,wEAAwE;QACxE,mBAAmB;QACnB,MAAM,OAAO,GAAG,MAAM,EAAE;aACnB,OAAO,CAAC,iCAAiC,CAAC;aAC1C,MAAM,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAA;QAEvD,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACvB,OAAO,MAAM,CAAA;QACjB,CAAC;QAED,IAAI,CAAC,OAAO,CAAC,iCAAiC,OAAO,CAAC,CAAC,CAAC,oBAAoB,CAAC,CAAA;QAE7E,IAAI,GAAG,EAAE,CAAC;YACN,OAAO,MAAM,CAAA;QACjB,CAAC;QAED,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,eAAe,EAAE,IAAI,CAAC,gBAAgB,EAAE,CAAC,CAAA;QAE5D,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;IACzB,CAAC;IAEO,gBAAgB;QACpB,OAAO,MAAM,EAAE,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,MAAM,CAAC,uBAAuB,CAAC,CAAC,CAAA;IACtE,CAAC;CACJ"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { policy } from '../policy.js';
|
|
2
|
+
import { Check } from './check.js';
|
|
3
|
+
export class HasNpmScriptsCheck extends Check {
|
|
4
|
+
static checkName = 'hasNpmScripts';
|
|
5
|
+
check() {
|
|
6
|
+
const packageJson = this.project.readJson('package.json');
|
|
7
|
+
if (packageJson === null) {
|
|
8
|
+
this.comment('Package.json missing: Create package.json in project root');
|
|
9
|
+
return 'fail';
|
|
10
|
+
}
|
|
11
|
+
for (const script of policy().strings('npmScripts.required')) {
|
|
12
|
+
if (packageJson.scripts?.[script] === undefined) {
|
|
13
|
+
this.comment(`Missing ${script} script in package.json: Add "${script}" to scripts section`);
|
|
14
|
+
return 'fail';
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
return 'pass';
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=has-npm-scripts-check.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"has-npm-scripts-check.js","sourceRoot":"","sources":["../../src/checks/has-npm-scripts-check.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AACrC,OAAO,EAAE,KAAK,EAAoB,MAAM,YAAY,CAAA;AAMpD,MAAM,OAAO,kBAAmB,SAAQ,KAAK;IACzC,MAAM,CAAmB,SAAS,GAAG,eAAe,CAAA;IAEpD,KAAK;QACD,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAc,cAAc,CAAC,CAAA;QAEtE,IAAI,WAAW,KAAK,IAAI,EAAE,CAAC;YACvB,IAAI,CAAC,OAAO,CAAC,2DAA2D,CAAC,CAAA;YAEzE,OAAO,MAAM,CAAA;QACjB,CAAC;QAED,KAAK,MAAM,MAAM,IAAI,MAAM,EAAE,CAAC,OAAO,CAAC,qBAAqB,CAAC,EAAE,CAAC;YAC3D,IAAI,WAAW,CAAC,OAAO,EAAE,CAAC,MAAM,CAAC,KAAK,SAAS,EAAE,CAAC;gBAC9C,IAAI,CAAC,OAAO,CAAC,WAAW,MAAM,iCAAiC,MAAM,sBAAsB,CAAC,CAAA;gBAE5F,OAAO,MAAM,CAAA;YACjB,CAAC;QACL,CAAC;QAED,OAAO,MAAM,CAAA;IACjB,CAAC;CACJ"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { type CheckResult, FixableCheck } from './check.js';
|
|
2
|
+
/**
|
|
3
|
+
* Trivy's setup, identical to the Laravel runner's: the security CI job, the
|
|
4
|
+
* canonical trivy.yaml, an ignore file, and the cache directory in .gitignore.
|
|
5
|
+
*
|
|
6
|
+
* The canonical config skips paths a JS project does not have (`vendor/**`,
|
|
7
|
+
* `storage/logs/`, `.ddev/`). Skipping a directory that is not there costs
|
|
8
|
+
* nothing, and one shared template means the two runners cannot drift — so the
|
|
9
|
+
* npm runner requires the same file rather than a trimmed variant.
|
|
10
|
+
*/
|
|
11
|
+
export declare class HasTrivyConfigCheck extends FixableCheck {
|
|
12
|
+
static readonly checkName = "hasTrivyConfig";
|
|
13
|
+
fix(dry?: boolean): CheckResult;
|
|
14
|
+
/** The canonical body, written verbatim into a project that has none. */
|
|
15
|
+
private canonicalConfig;
|
|
16
|
+
/**
|
|
17
|
+
* The canonical config read as a requirement set: every scalar leaf must be
|
|
18
|
+
* equal in the project's file, every list leaf contained in it. Keys the
|
|
19
|
+
* template does not mention are the project's own business.
|
|
20
|
+
*/
|
|
21
|
+
private requirements;
|
|
22
|
+
private getByPath;
|
|
23
|
+
/** Adds each missing job, leaving the rest of the pipeline untouched. */
|
|
24
|
+
private addMissingCiJobs;
|
|
25
|
+
private ensureGitignoreEntry;
|
|
26
|
+
}
|