@freshworks/shiftleft-tools 1.1.12 → 1.1.14
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/README.md +4 -3
- package/package.json +1 -1
- package/src/commands/init-rules.js +4 -35
- package/src/commands/update.js +3 -10
- package/templates/postman/scripts/run-all.sh +11 -5
- package/templates/postman-node/scripts/run-all.sh +10 -4
- package/templates/skills/_shared/guardrails.md +44 -0
- package/templates/skills/enhance-test-pipeline/SKILL-java.md +2 -0
- package/templates/skills/enhance-test-pipeline/SKILL-node.md +2 -0
- package/templates/skills/setup-test-pipeline/SKILL-java.md +2 -0
- package/templates/skills/setup-test-pipeline/SKILL-node.md +2 -0
- package/templates/AGENTS.md +0 -109
- package/templates/CLAUDE.md +0 -3
package/README.md
CHANGED
|
@@ -94,7 +94,7 @@ shiftleft init-rules
|
|
|
94
94
|
```
|
|
95
95
|
|
|
96
96
|
Options:
|
|
97
|
-
- `--force, -f` - Overwrite existing
|
|
97
|
+
- `--force, -f` - Overwrite existing managed files
|
|
98
98
|
- `--skip-skills` - Link rules only, not Cursor skills
|
|
99
99
|
- `--stack <java|node>` - Project stack (auto-detected from `pom.xml` or `package.json`)
|
|
100
100
|
- `--copy` - Copy assets instead of symlinking (locked-down environments / Windows without symlink permission)
|
|
@@ -106,11 +106,12 @@ This creates:
|
|
|
106
106
|
│ ├── testing.mdc -> (symlink) package rule for your stack
|
|
107
107
|
│ └── local-test-setup.mdc -> (symlink) Java only
|
|
108
108
|
└── skills/ -> (symlink) package skills directory
|
|
109
|
-
CLAUDE.md # Skill invocation instructions for Claude Code (real file)
|
|
110
|
-
AGENTS.md # Agent routing and guardrails (real file)
|
|
111
109
|
.gitignore # symlink paths added here (not committed)
|
|
112
110
|
```
|
|
113
111
|
|
|
112
|
+
Skill routing and security guardrails live in the skills themselves
|
|
113
|
+
(`skills/_shared/guardrails.md`) — no per-repo `CLAUDE.md` / `AGENTS.md` is written.
|
|
114
|
+
|
|
114
115
|
> Migrating from an older version? A committed `.claude/skills/` directory is no
|
|
115
116
|
> longer needed — install the plugin and delete it. `shiftleft doctor` flags it.
|
|
116
117
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@freshworks/shiftleft-tools",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.14",
|
|
4
4
|
"description": "CLI for managing Cursor rules/skills and Postman test infrastructure across Java Spring Boot and Node.js/Express projects",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.js",
|
|
@@ -1,17 +1,16 @@
|
|
|
1
|
-
import { join } from 'path';
|
|
2
|
-
import { copyTemplate } from '../utils/template.js';
|
|
3
1
|
import { detectStack } from '../utils/stack.js';
|
|
4
|
-
import { loadManifest, createManifest, saveManifest
|
|
2
|
+
import { loadManifest, createManifest, saveManifest } from '../utils/manifest.js';
|
|
5
3
|
import { linkAssets } from './link.js';
|
|
6
4
|
import { logger } from '../utils/logger.js';
|
|
7
5
|
|
|
8
6
|
/**
|
|
9
|
-
* Initialize Cursor rules/skills
|
|
7
|
+
* Initialize Cursor rules/skills in the project.
|
|
10
8
|
*
|
|
11
9
|
* Cursor skills and rules are linked to the installed shiftleft-tools package
|
|
12
10
|
* (symlinks, gitignored) so they update with a single global install. Claude
|
|
13
11
|
* Code skills are provided by the shiftleft-tools plugin, not copied per repo.
|
|
14
|
-
* CLAUDE.md / AGENTS.md
|
|
12
|
+
* No CLAUDE.md / AGENTS.md are written — skill routing and security guardrails
|
|
13
|
+
* live in the skills themselves (see skills/_shared/guardrails.md).
|
|
15
14
|
*/
|
|
16
15
|
export async function initRules(options) {
|
|
17
16
|
const cwd = process.cwd();
|
|
@@ -34,9 +33,6 @@ export async function initRules(options) {
|
|
|
34
33
|
logger.success(`Copied ${copied.length} Cursor asset(s) (symlinks unavailable)`);
|
|
35
34
|
}
|
|
36
35
|
|
|
37
|
-
copyClaude(cwd, options, manifest);
|
|
38
|
-
copyAgents(cwd, options, manifest);
|
|
39
|
-
|
|
40
36
|
saveManifest(cwd, manifest);
|
|
41
37
|
|
|
42
38
|
console.log();
|
|
@@ -49,30 +45,3 @@ export async function initRules(options) {
|
|
|
49
45
|
}
|
|
50
46
|
}
|
|
51
47
|
|
|
52
|
-
/** Record a copyTemplate result in the manifest when it actually wrote a file. */
|
|
53
|
-
function record(manifest, cwd, templatePath, result) {
|
|
54
|
-
if (result && !result.skipped) {
|
|
55
|
-
recordFile(manifest, cwd, result.path, result.content, { template: templatePath });
|
|
56
|
-
}
|
|
57
|
-
return result;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
function copyClaude(cwd, options, manifest) {
|
|
61
|
-
const claudeMdDest = join(cwd, 'CLAUDE.md');
|
|
62
|
-
const result = record(manifest, cwd, 'CLAUDE.md', copyTemplate('CLAUDE.md', claudeMdDest, {}, { force: options.force }));
|
|
63
|
-
if (result.skipped) {
|
|
64
|
-
logger.skip('Skipped CLAUDE.md (already exists, use --force to overwrite)');
|
|
65
|
-
} else {
|
|
66
|
-
logger.success('Created CLAUDE.md');
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
function copyAgents(cwd, options, manifest) {
|
|
71
|
-
const agentsMdDest = join(cwd, 'AGENTS.md');
|
|
72
|
-
const result = record(manifest, cwd, 'AGENTS.md', copyTemplate('AGENTS.md', agentsMdDest, {}, { force: options.force }));
|
|
73
|
-
if (result.skipped) {
|
|
74
|
-
logger.skip('Skipped AGENTS.md (already exists, use --force to overwrite)');
|
|
75
|
-
} else {
|
|
76
|
-
logger.success('Created AGENTS.md');
|
|
77
|
-
}
|
|
78
|
-
}
|
package/src/commands/update.js
CHANGED
|
@@ -67,7 +67,7 @@ export async function update(options) {
|
|
|
67
67
|
const totals = { copied: 0, skipped: 0, conflicts: 0 };
|
|
68
68
|
|
|
69
69
|
if (updateRules) {
|
|
70
|
-
accumulate(totals, await updateRulesFiles(cwd, manifest
|
|
70
|
+
accumulate(totals, await updateRulesFiles(cwd, manifest));
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
if (updateSkills) {
|
|
@@ -105,7 +105,7 @@ function tally(result, status) {
|
|
|
105
105
|
else result.copied++;
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
-
async function updateRulesFiles(cwd, manifest
|
|
108
|
+
async function updateRulesFiles(cwd, manifest) {
|
|
109
109
|
const spinner = ora('Updating rules...').start();
|
|
110
110
|
const result = { copied: 0, skipped: 0, conflicts: 0 };
|
|
111
111
|
|
|
@@ -121,14 +121,7 @@ async function updateRulesFiles(cwd, manifest, force, adopt) {
|
|
|
121
121
|
result.skipped += linked.length;
|
|
122
122
|
result.copied += copied.length;
|
|
123
123
|
|
|
124
|
-
|
|
125
|
-
const claudeMdDest = join(cwd, 'CLAUDE.md');
|
|
126
|
-
if (existsSync(claudeMdDest)) {
|
|
127
|
-
tally(result, syncTemplate('CLAUDE.md', claudeMdDest, {}, { manifest, cwd, force, adopt }).status);
|
|
128
|
-
}
|
|
129
|
-
tally(result, syncTemplate('AGENTS.md', join(cwd, 'AGENTS.md'), {}, { manifest, cwd, force, adopt }).status);
|
|
130
|
-
|
|
131
|
-
spinner.succeed(`Rules: ${linked.length} linked, ${copied.length} copied, CLAUDE/AGENTS ${result.conflicts ? `${result.conflicts} conflict(s)` : 'in sync'}`);
|
|
124
|
+
spinner.succeed(`Rules: ${linked.length} linked, ${copied.length} copied`);
|
|
132
125
|
return result;
|
|
133
126
|
}
|
|
134
127
|
|
|
@@ -400,11 +400,17 @@ fi
|
|
|
400
400
|
# links to a stale consolidated report whose per-collection detail files have
|
|
401
401
|
# already been cleaned up. Only fall back to the newest existing one if this
|
|
402
402
|
# run produced no consolidated JSON to render.
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
403
|
+
# The Postman runner self-timestamps its artifacts, so the consolidated JSON's
|
|
404
|
+
# timestamp differs from this orchestrator's $TIMESTAMP. Derive the HTML name
|
|
405
|
+
# from the actual consolidated JSON (resolved above) rather than $TIMESTAMP.
|
|
406
|
+
POSTMAN_CONSOLIDATED_HTML=""
|
|
407
|
+
if [ -n "$CONSOLIDATED_JSON" ] && [ -f "$CONSOLIDATED_JSON" ]; then
|
|
408
|
+
CONSOLIDATED_TS=$(basename "$CONSOLIDATED_JSON" .json | sed "s/^consolidated-${POSTMAN_ENV}-//")
|
|
409
|
+
POSTMAN_CONSOLIDATED_HTML="$REPORTS_DIR/consolidated-${POSTMAN_ENV}-${CONSOLIDATED_TS}.html"
|
|
410
|
+
if [ ! -f "$POSTMAN_CONSOLIDATED_HTML" ]; then
|
|
411
|
+
echo -e "${YELLOW}Generating consolidated HTML report...${NC}"
|
|
412
|
+
"$SCRIPT_DIR/report-generators/generate-consolidated-report.sh" "$POSTMAN_ENV" "$CONSOLIDATED_TS" "$REPORTS_DIR" "$POSTMAN_PASSED" "$POSTMAN_FAILED" 0 >/dev/null 2>&1 || true
|
|
413
|
+
fi
|
|
408
414
|
fi
|
|
409
415
|
if [ ! -f "$POSTMAN_CONSOLIDATED_HTML" ]; then
|
|
410
416
|
POSTMAN_CONSOLIDATED_HTML=$(ls -t "$REPORTS_DIR"/consolidated-*.html 2>/dev/null | head -1 || true)
|
|
@@ -276,10 +276,16 @@ if [ "$SKIP_REPORT" = false ]; then
|
|
|
276
276
|
# combined report never links to a stale consolidated whose per-collection
|
|
277
277
|
# detail files have been cleaned up. Fall back to the newest only if this run
|
|
278
278
|
# produced no consolidated JSON to render.
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
279
|
+
# The Postman runner self-timestamps, so the consolidated JSON's timestamp
|
|
280
|
+
# differs from this orchestrator's $TIMESTAMP. Derive the HTML name from the
|
|
281
|
+
# actual consolidated JSON rather than $TIMESTAMP.
|
|
282
|
+
POSTMAN_CONSOLIDATED_HTML=""
|
|
283
|
+
if [ -n "$CONSOLIDATED_JSON" ] && [ -f "$CONSOLIDATED_JSON" ]; then
|
|
284
|
+
CONSOLIDATED_TS=$(basename "$CONSOLIDATED_JSON" .json | sed "s/^consolidated-${POSTMAN_ENV}-//")
|
|
285
|
+
POSTMAN_CONSOLIDATED_HTML="$REPORTS_DIR/consolidated-${POSTMAN_ENV}-${CONSOLIDATED_TS}.html"
|
|
286
|
+
if [ ! -f "$POSTMAN_CONSOLIDATED_HTML" ]; then
|
|
287
|
+
"$SCRIPT_DIR/report-generators/generate-consolidated-report.sh" "$POSTMAN_ENV" "$CONSOLIDATED_TS" "$REPORTS_DIR" "$POSTMAN_PASSED" "$POSTMAN_FAILED" 0 >/dev/null 2>&1 || true
|
|
288
|
+
fi
|
|
283
289
|
fi
|
|
284
290
|
if [ ! -f "$POSTMAN_CONSOLIDATED_HTML" ]; then
|
|
285
291
|
POSTMAN_CONSOLIDATED_HTML=$(ls -t "$REPORTS_DIR"/consolidated-*.html 2>/dev/null | head -1 || true)
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Security Guardrails — Non-negotiables
|
|
2
|
+
|
|
3
|
+
These apply to every repo this tooling touches. Enforce them regardless of which
|
|
4
|
+
skill is running and regardless of whether the user asked. If any are violated,
|
|
5
|
+
fix them as part of the work.
|
|
6
|
+
|
|
7
|
+
## Node security guardrails
|
|
8
|
+
|
|
9
|
+
**`.npmrc` must exist** in the `postman/` directory with:
|
|
10
|
+
```ini
|
|
11
|
+
ignore-scripts=true # blocks postinstall supply chain attacks
|
|
12
|
+
audit-level=high
|
|
13
|
+
audit-signatures=true # verifies package signing (npm 8.4+)
|
|
14
|
+
strict-ssl=true
|
|
15
|
+
registry=https://registry.npmjs.org/
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
**`package-lock.json` must exist** — never `npm install` without a lockfile. In CI always use `npm ci --ignore-scripts`, never `npm install`.
|
|
19
|
+
|
|
20
|
+
**Secrets must never reach npm packages:**
|
|
21
|
+
- AWS credentials: fetch secret → generate JWT → `unset` the secret variable immediately before running Newman
|
|
22
|
+
- `GH_TOKEN` / `GITHUB_RUNWAYCI_TOKEN`: only inside `withCredentials` blocks, never in `environment {}` block
|
|
23
|
+
- Never pass secrets as environment variables to `npm run` commands
|
|
24
|
+
|
|
25
|
+
**npm audit in Jenkins:** Every ShiftLeft / staging test run must include:
|
|
26
|
+
```bash
|
|
27
|
+
npm ci --ignore-scripts
|
|
28
|
+
npm audit --audit-level=critical 2>&1 || echo "WARNING: audit found issues"
|
|
29
|
+
npm audit signatures 2>&1 || echo "WARNING: signature verification failed"
|
|
30
|
+
npm audit --json > ../audit-report.json 2>&1 || true
|
|
31
|
+
archiveArtifacts artifacts: 'audit-report.json', allowEmptyArchive: true
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Java security guardrails
|
|
35
|
+
|
|
36
|
+
- Never commit secrets or credentials in `pom.xml` or `application.properties`
|
|
37
|
+
- `application-test.properties` / `application-integration.properties`: H2 only, never real DB credentials
|
|
38
|
+
- PIT mutation tests must be in a `<profile>` — never in the main build (prevents accidental slow runs)
|
|
39
|
+
|
|
40
|
+
## Jenkins guardrails (both Java and Node)
|
|
41
|
+
|
|
42
|
+
- IRSA (pod IAM role) for AWS — never static `AWS_ACCESS_KEY_ID` / `AWS_SECRET_ACCESS_KEY` in Jenkins env
|
|
43
|
+
- `withCredentials` scope: credentials exposed only for the specific `sh` block that needs them, not the whole stage
|
|
44
|
+
- `deleteDir()` in `post { always }` — clean workspace after every build
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
Detect gaps and fill them in a Java/Spring Boot repo that ALREADY HAS some test pipeline pieces but not all. The key difference from `/setup-test-pipeline` is being careful NOT to overwrite existing configs that are working correctly.
|
|
4
4
|
|
|
5
|
+
> **Security guardrails (non-negotiable):** audit against `../_shared/guardrails.md` (Node/Java/Jenkins) and fix any violations as part of this skill, regardless of what was asked.
|
|
6
|
+
|
|
5
7
|
## When to Use
|
|
6
8
|
|
|
7
9
|
Invoke when:
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
Detect gaps and fill them in a Node.js/Express repo that ALREADY HAS some test pipeline pieces but not all. The key difference from `/setup-test-pipeline` is being careful NOT to overwrite existing configs that are working correctly.
|
|
4
4
|
|
|
5
|
+
> **Security guardrails (non-negotiable):** audit against `../_shared/guardrails.md` (Node/Java/Jenkins) and fix any violations as part of this skill, regardless of what was asked.
|
|
6
|
+
|
|
5
7
|
## When to Use
|
|
6
8
|
|
|
7
9
|
Invoke when:
|
|
@@ -4,6 +4,8 @@ Set up a complete test pipeline from scratch (or near-scratch) for a Java/Spring
|
|
|
4
4
|
|
|
5
5
|
Use this skill when a repo has NOTHING (or only bare unit tests). For repos that already have partial pipeline setup, use `/enhance-test-pipeline` instead.
|
|
6
6
|
|
|
7
|
+
> **Security guardrails (non-negotiable):** enforce everything in `../_shared/guardrails.md` (Node/Java/Jenkins) as part of this skill, regardless of what was asked.
|
|
8
|
+
|
|
7
9
|
## When to Use
|
|
8
10
|
|
|
9
11
|
Invoke when:
|
|
@@ -4,6 +4,8 @@ Set up a complete test pipeline from scratch (or near-scratch) for a Node.js/Exp
|
|
|
4
4
|
|
|
5
5
|
Use this skill when a repo has NOTHING (or only bare unit tests). For repos that already have partial pipeline setup, use `/enhance-test-pipeline` instead.
|
|
6
6
|
|
|
7
|
+
> **Security guardrails (non-negotiable):** enforce everything in `../_shared/guardrails.md` (Node/Java/Jenkins) as part of this skill, regardless of what was asked.
|
|
8
|
+
|
|
7
9
|
## When to Use
|
|
8
10
|
|
|
9
11
|
Invoke when:
|
package/templates/AGENTS.md
DELETED
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
# Agent Instructions — Freshworks Test Pipeline Dev Tools
|
|
2
|
-
|
|
3
|
-
This file is the agent entry point for AI assistants (Claude Code, Cursor, Codex) working in a Freshworks platform service repo. It routes to the right skill based on what needs to be done.
|
|
4
|
-
|
|
5
|
-
## How to use skills
|
|
6
|
-
|
|
7
|
-
When the user invokes `/skill-name`:
|
|
8
|
-
1. Read `.claude/skills/<skill-name>/SKILL.md` (Claude Code) or `.cursor/skills/<skill-name>/SKILL.md` (Cursor)
|
|
9
|
-
2. Follow the instructions in that file completely — skills are step-by-step agent workflows, not static templates
|
|
10
|
-
3. Skills inspect the repo first, reason about what's needed, confirm with the user, then execute
|
|
11
|
-
|
|
12
|
-
**Never copy-paste from templates without first reading the actual repo structure.** Every project has unique package names, module layouts, port numbers, and test patterns.
|
|
13
|
-
|
|
14
|
-
---
|
|
15
|
-
|
|
16
|
-
## Which skill to use
|
|
17
|
-
|
|
18
|
-
| Goal | Skill | Notes |
|
|
19
|
-
|---|---|---|
|
|
20
|
-
| New repo — set up everything from scratch | `/setup-test-pipeline` | Covers unit tests, JaCoCo/nyc, mutation, Postman, API coverage, quality report, Jenkins |
|
|
21
|
-
| Existing repo — find and fill gaps | `/enhance-test-pipeline` | Audits what's present, adds only what's missing, checks security guardrails |
|
|
22
|
-
| Add mutation tests only | `/setup-mutation-tests` | PIT for Java, Stryker for Node — discovers actual package scope before configuring |
|
|
23
|
-
| Set up Postman/Newman infrastructure | `/setup-api-tests` | Collections, environments, scripts (Java: WireMock; Node: nock) |
|
|
24
|
-
| Write tests for specific endpoints | `/write-api-tests [METHOD /path]` | e.g. `/write-api-tests POST /api/v3/installations` |
|
|
25
|
-
| Run tests | `/run-test-suite` | Knows all `run-all.sh` flags, CI stage-by-stage patterns |
|
|
26
|
-
| Review test quality | `/review-test-suite` | 20-point maturity check |
|
|
27
|
-
|
|
28
|
-
---
|
|
29
|
-
|
|
30
|
-
## Auto-detect: no skill specified
|
|
31
|
-
|
|
32
|
-
If the user asks to "set up tests", "improve the pipeline", or similar without specifying a skill, inspect the repo first and decide:
|
|
33
|
-
|
|
34
|
-
```bash
|
|
35
|
-
# Is this Java or Node?
|
|
36
|
-
ls pom.xml 2>/dev/null && echo "Java" || echo "Node"
|
|
37
|
-
|
|
38
|
-
# What pipeline pieces already exist?
|
|
39
|
-
ls postman/scripts/run-all.sh 2>/dev/null && echo "run-all.sh present"
|
|
40
|
-
grep -l "pitest-maven" pom.xml 2>/dev/null && echo "PIT present" # Java
|
|
41
|
-
grep '"mutation-tests"' package.json 2>/dev/null && echo "Stryker present" # Node
|
|
42
|
-
ls postman/scripts/report-generators/java-api-coverage-matrix.sh 2>/dev/null && echo "Java API coverage present"
|
|
43
|
-
ls postman/scripts/report-generators/node-api-coverage-matrix.sh 2>/dev/null && echo "Node API coverage present"
|
|
44
|
-
ls postman/reports/ 2>/dev/null && echo "Reports present"
|
|
45
|
-
grep -l "Quality Report\|generateQualityReport" Jenkinsfile 2>/dev/null && echo "Jenkins quality report present"
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
- **Nothing exists** → use `/setup-test-pipeline`
|
|
49
|
-
- **Some things exist** → use `/enhance-test-pipeline`
|
|
50
|
-
- **Specific component missing** → use the targeted skill
|
|
51
|
-
|
|
52
|
-
---
|
|
53
|
-
|
|
54
|
-
## Non-negotiables (always enforce, regardless of skill)
|
|
55
|
-
|
|
56
|
-
These apply to every repo this tooling touches. If any of these are violated, fix them as part of the work — do not skip even if the user didn't ask.
|
|
57
|
-
|
|
58
|
-
### Node security guardrails
|
|
59
|
-
|
|
60
|
-
**`.npmrc` must exist** in the `postman/` directory with:
|
|
61
|
-
```ini
|
|
62
|
-
ignore-scripts=true # blocks postinstall supply chain attacks
|
|
63
|
-
audit-level=high
|
|
64
|
-
audit-signatures=true # verifies package signing (npm 8.4+)
|
|
65
|
-
strict-ssl=true
|
|
66
|
-
registry=https://registry.npmjs.org/
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
**`package-lock.json` must exist** — never `npm install` without a lockfile. In CI always use `npm ci --ignore-scripts`, never `npm install`.
|
|
70
|
-
|
|
71
|
-
**Secrets must never reach npm packages:**
|
|
72
|
-
- AWS credentials: fetch secret → generate JWT → `unset` the secret variable immediately before running Newman
|
|
73
|
-
- `GH_TOKEN` / `GITHUB_RUNWAYCI_TOKEN`: only inside `withCredentials` blocks, never in `environment {}` block
|
|
74
|
-
- Never pass secrets as environment variables to `npm run` commands
|
|
75
|
-
|
|
76
|
-
**npm audit in Jenkins:** Every ShiftLeft / staging test run must include:
|
|
77
|
-
```bash
|
|
78
|
-
npm ci --ignore-scripts
|
|
79
|
-
npm audit --audit-level=critical 2>&1 || echo "WARNING: audit found issues"
|
|
80
|
-
npm audit signatures 2>&1 || echo "WARNING: signature verification failed"
|
|
81
|
-
npm audit --json > ../audit-report.json 2>&1 || true
|
|
82
|
-
archiveArtifacts artifacts: 'audit-report.json', allowEmptyArchive: true
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
### Java security guardrails
|
|
86
|
-
|
|
87
|
-
- Never commit secrets or credentials in `pom.xml` or `application.properties`
|
|
88
|
-
- `application-test.properties` / `application-integration.properties`: H2 only, never real DB credentials
|
|
89
|
-
- PIT mutation tests must be in a `<profile>` — never in the main build (prevents accidental slow runs)
|
|
90
|
-
|
|
91
|
-
### Jenkins guardrails (both Java and Node)
|
|
92
|
-
|
|
93
|
-
- IRSA (pod IAM role) for AWS — never static `AWS_ACCESS_KEY_ID` / `AWS_SECRET_ACCESS_KEY` in Jenkins env
|
|
94
|
-
- `withCredentials` scope: credentials exposed only for the specific `sh` block that needs them, not the whole stage
|
|
95
|
-
- `deleteDir()` in `post { always }` — clean workspace after every build
|
|
96
|
-
|
|
97
|
-
---
|
|
98
|
-
|
|
99
|
-
## Reference implementations
|
|
100
|
-
|
|
101
|
-
These repos are the canonical examples of a fully set-up pipeline:
|
|
102
|
-
|
|
103
|
-
| Repo | Type | What it demonstrates |
|
|
104
|
-
|---|---|---|
|
|
105
|
-
| `mp-installation` | Java/Spring Boot | PIT mutation, JaCoCo, multi-product Postman, API coverage matrix, quality report, Jenkins ShiftLeft |
|
|
106
|
-
| `freshapps_api_node` | Node/Express | Stryker since/full modes, nyc coverage, Newman staging, node-api-coverage-matrix, quality report |
|
|
107
|
-
| `dp-apps` | Java/Spring Boot | Same as mp-installation — the original reference |
|
|
108
|
-
|
|
109
|
-
When a skill needs a concrete example of a script, config, or Jenkinsfile stage, refer to these repos.
|
package/templates/CLAUDE.md
DELETED