@freshworks/shiftleft-tools 1.1.8
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 +351 -0
- package/bin/shiftleft.js +95 -0
- package/package.json +57 -0
- package/src/commands/doctor.js +208 -0
- package/src/commands/init-postman.js +298 -0
- package/src/commands/init-rules.js +78 -0
- package/src/commands/link.js +172 -0
- package/src/commands/protect.js +61 -0
- package/src/commands/run-tests.js +182 -0
- package/src/commands/setup-pipeline.js +209 -0
- package/src/commands/update.js +203 -0
- package/src/index.js +4 -0
- package/src/utils/copy-tree.js +98 -0
- package/src/utils/gitignore.js +26 -0
- package/src/utils/logger.js +9 -0
- package/src/utils/manifest.js +145 -0
- package/src/utils/stack.js +80 -0
- package/src/utils/template.js +135 -0
- package/templates/AGENTS.md +109 -0
- package/templates/CLAUDE.md +3 -0
- package/templates/jenkins/Jenkinsfile-java.groovy +432 -0
- package/templates/jenkins/Jenkinsfile-node.groovy +450 -0
- package/templates/postman/.husky/pre-commit +19 -0
- package/templates/postman/.prettierrc.json +5 -0
- package/templates/postman/README.md.ejs +147 -0
- package/templates/postman/collections/01-core.json.ejs +91 -0
- package/templates/postman/config/local.json.ejs +12 -0
- package/templates/postman/config/staging.json.ejs +26 -0
- package/templates/postman/environments/local.postman_environment.json.ejs +31 -0
- package/templates/postman/environments/staging.postman_environment.json.ejs +31 -0
- package/templates/postman/gitignore +16 -0
- package/templates/postman/npmrc +31 -0
- package/templates/postman/package.json.ejs +66 -0
- package/templates/postman/run-all-shim.sh +16 -0
- package/templates/postman/scripts/auth/generate-jwt.sh +113 -0
- package/templates/postman/scripts/auth/get-issuer-secret.sh +140 -0
- package/templates/postman/scripts/infra/start-mocks.sh +138 -0
- package/templates/postman/scripts/infra/stop-mocks.sh +43 -0
- package/templates/postman/scripts/lib/api_coverage.py +1122 -0
- package/templates/postman/scripts/lib/cleanup-reports.sh +101 -0
- package/templates/postman/scripts/lib/cleanup-stryker.sh +44 -0
- package/templates/postman/scripts/lib/report_combined.py +527 -0
- package/templates/postman/scripts/lib/report_consolidated.py +363 -0
- package/templates/postman/scripts/lib/report_generator.py +121 -0
- package/templates/postman/scripts/lib/report_migration.py +156 -0
- package/templates/postman/scripts/lib/report_mutation.py +110 -0
- package/templates/postman/scripts/lib/report_unit.py +353 -0
- package/templates/postman/scripts/lib/report_utils.py +973 -0
- package/templates/postman/scripts/report-generators/generate-consolidated-report.sh +445 -0
- package/templates/postman/scripts/report-generators/java-api-coverage-matrix.sh +257 -0
- package/templates/postman/scripts/report-generators/mutation-report.sh +672 -0
- package/templates/postman/scripts/report-generators/node-api-coverage-matrix.sh +167 -0
- package/templates/postman/scripts/report-generators/stage-report-artifacts.sh +27 -0
- package/templates/postman/scripts/run-all.sh +452 -0
- package/templates/postman/scripts/runners/run-mutation-tests.sh +113 -0
- package/templates/postman/scripts/runners/run-tests-local.sh +936 -0
- package/templates/postman/scripts/runners/run-tests-staging.sh +741 -0
- package/templates/postman-node/README.md.ejs +26 -0
- package/templates/postman-node/collections/crud/01-bootstrap.json.ejs +34 -0
- package/templates/postman-node/config/local.json.ejs +46 -0
- package/templates/postman-node/config/staging.json.ejs +31 -0
- package/templates/postman-node/local.test.env.ejs +3 -0
- package/templates/postman-node/mocks/external.js +14 -0
- package/templates/postman-node/package.json.ejs +39 -0
- package/templates/postman-node/requirements.txt +1 -0
- package/templates/postman-node/scripts/database/cleanup-mysql.sh +12 -0
- package/templates/postman-node/scripts/database/run-migrations.js +29 -0
- package/templates/postman-node/scripts/database/start-mysql.sh +34 -0
- package/templates/postman-node/scripts/database/wait-for-mysql.sh +36 -0
- package/templates/postman-node/scripts/lib/api_coverage_node.py +1137 -0
- package/templates/postman-node/scripts/lib/fetch-jwt.sh +86 -0
- package/templates/postman-node/scripts/lib/run-newman.sh +104 -0
- package/templates/postman-node/scripts/lib/setup-database.sh +55 -0
- package/templates/postman-node/scripts/lib/start-app.sh +48 -0
- package/templates/postman-node/scripts/lib/utils.sh +114 -0
- package/templates/postman-node/scripts/report-generators/stage-report-artifacts.sh +26 -0
- package/templates/postman-node/scripts/run-all.sh +303 -0
- package/templates/postman-node/scripts/runners/run-tests.sh +123 -0
- package/templates/postman-node/scripts/setup-mocks.js.ejs +29 -0
- package/templates/postman-node/stryker.config.js.ejs +51 -0
- package/templates/rules/local-test-setup.mdc +420 -0
- package/templates/rules/testing-node.mdc +66 -0
- package/templates/rules/testing.mdc +248 -0
- package/templates/skills/_shared/postman-standards.md +380 -0
- package/templates/skills/enhance-test-pipeline/SKILL-java.md +483 -0
- package/templates/skills/enhance-test-pipeline/SKILL-node.md +431 -0
- package/templates/skills/enhance-test-pipeline/SKILL.md +9 -0
- package/templates/skills/review-test-suite/SKILL-java.md +137 -0
- package/templates/skills/review-test-suite/SKILL-node.md +78 -0
- package/templates/skills/review-test-suite/SKILL.md +9 -0
- package/templates/skills/run-test-suite/SKILL-java.md +186 -0
- package/templates/skills/run-test-suite/SKILL-node.md +191 -0
- package/templates/skills/run-test-suite/SKILL.md +9 -0
- package/templates/skills/setup-api-tests/SKILL-java.md +1094 -0
- package/templates/skills/setup-api-tests/SKILL-node.md +141 -0
- package/templates/skills/setup-api-tests/SKILL.md +9 -0
- package/templates/skills/setup-mutation-tests/SKILL-java.md +303 -0
- package/templates/skills/setup-mutation-tests/SKILL-node.md +408 -0
- package/templates/skills/setup-mutation-tests/SKILL.md +9 -0
- package/templates/skills/setup-test-pipeline/SKILL-java.md +454 -0
- package/templates/skills/setup-test-pipeline/SKILL-node.md +318 -0
- package/templates/skills/setup-test-pipeline/SKILL.md +9 -0
- package/templates/skills/write-api-tests/SKILL-java.md +115 -0
- package/templates/skills/write-api-tests/SKILL-node.md +83 -0
- package/templates/skills/write-api-tests/SKILL.md +9 -0
- package/templates/stryker.config.js +50 -0
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
{
|
|
2
|
+
"info": {
|
|
3
|
+
"_postman_id": "01-core-<%= serviceNameLower %>",
|
|
4
|
+
"name": "Core API",
|
|
5
|
+
"description": "Core API endpoints for <%= serviceName %>",
|
|
6
|
+
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
|
|
7
|
+
},
|
|
8
|
+
"auth": {
|
|
9
|
+
"type": "bearer",
|
|
10
|
+
"bearer": [
|
|
11
|
+
{
|
|
12
|
+
"key": "token",
|
|
13
|
+
"value": "{{auth_token}}",
|
|
14
|
+
"type": "string"
|
|
15
|
+
}
|
|
16
|
+
]
|
|
17
|
+
},
|
|
18
|
+
"event": [
|
|
19
|
+
{
|
|
20
|
+
"listen": "prerequest",
|
|
21
|
+
"script": {
|
|
22
|
+
"type": "text/javascript",
|
|
23
|
+
"exec": [""]
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"listen": "test",
|
|
28
|
+
"script": {
|
|
29
|
+
"type": "text/javascript",
|
|
30
|
+
"exec": [""]
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
],
|
|
34
|
+
"item": [
|
|
35
|
+
{
|
|
36
|
+
"name": "Health Check",
|
|
37
|
+
"item": [
|
|
38
|
+
{
|
|
39
|
+
"name": "Health Check - 200 OK",
|
|
40
|
+
"event": [
|
|
41
|
+
{
|
|
42
|
+
"listen": "test",
|
|
43
|
+
"script": {
|
|
44
|
+
"exec": [
|
|
45
|
+
"pm.test(\"Status code is 200\", function () {",
|
|
46
|
+
" pm.response.to.have.status(200);",
|
|
47
|
+
"});",
|
|
48
|
+
"",
|
|
49
|
+
"pm.test(\"Response time is less than 2000ms\", function () {",
|
|
50
|
+
" pm.expect(pm.response.responseTime).to.be.below(2000);",
|
|
51
|
+
"});",
|
|
52
|
+
"",
|
|
53
|
+
"pm.test(\"Content-Type is application/json\", function () {",
|
|
54
|
+
" pm.expect(pm.response.headers.get(\"Content-Type\")).to.include(\"application/json\");",
|
|
55
|
+
"});",
|
|
56
|
+
"",
|
|
57
|
+
"var jsonData = pm.response.json();",
|
|
58
|
+
"",
|
|
59
|
+
"pm.test(\"Response has status field\", function () {",
|
|
60
|
+
" pm.expect(jsonData).to.have.property('status');",
|
|
61
|
+
" pm.expect(jsonData.status).to.eql('UP');",
|
|
62
|
+
"});"
|
|
63
|
+
],
|
|
64
|
+
"type": "text/javascript"
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
],
|
|
68
|
+
"request": {
|
|
69
|
+
"method": "GET",
|
|
70
|
+
"header": [],
|
|
71
|
+
"url": {
|
|
72
|
+
"raw": "{{base_url}}/actuator/health",
|
|
73
|
+
"host": ["{{base_url}}"],
|
|
74
|
+
"path": ["actuator", "health"]
|
|
75
|
+
},
|
|
76
|
+
"description": "Health check endpoint to verify service is running"
|
|
77
|
+
},
|
|
78
|
+
"response": []
|
|
79
|
+
}
|
|
80
|
+
]
|
|
81
|
+
}
|
|
82
|
+
],
|
|
83
|
+
"variable": [
|
|
84
|
+
{
|
|
85
|
+
"key": "base_url",
|
|
86
|
+
"value": "http://localhost:8080/api",
|
|
87
|
+
"type": "string",
|
|
88
|
+
"description": "Base URL for the API"
|
|
89
|
+
}
|
|
90
|
+
]
|
|
91
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"comment": "Local environment configuration for <%= serviceName %> Postman tests",
|
|
3
|
+
"environment": "local",
|
|
4
|
+
"base_url": "http://localhost:8080/api",
|
|
5
|
+
"jwt": {
|
|
6
|
+
"use_hardcoded_token": true,
|
|
7
|
+
"hardcoded_token": "temporary_token"
|
|
8
|
+
},
|
|
9
|
+
"headers": {
|
|
10
|
+
"content_type": "application/json"
|
|
11
|
+
}
|
|
12
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"comment": "Staging environment configuration for <%= serviceName %> Postman tests",
|
|
3
|
+
"environment": "staging",
|
|
4
|
+
"base_url": "https://your-staging-url.freshworks.com/api",
|
|
5
|
+
"aws": {
|
|
6
|
+
"secretName": "staging_stack_secrets",
|
|
7
|
+
"region": "us-west-2",
|
|
8
|
+
"client": "your_client",
|
|
9
|
+
"issuer": "your_issuer"
|
|
10
|
+
},
|
|
11
|
+
"jwt": {
|
|
12
|
+
"use_hardcoded_token": false,
|
|
13
|
+
"api_issuer": "your_issuer",
|
|
14
|
+
"api_account_id": "your_account_id",
|
|
15
|
+
"api_account_domain": "your_domain.freshworks.com",
|
|
16
|
+
"api_user_id": "your_user_id",
|
|
17
|
+
"api_product": "freshdesk",
|
|
18
|
+
"api_product_id": "1",
|
|
19
|
+
"api_user_role": "admin",
|
|
20
|
+
"api_pod": "poduswest2",
|
|
21
|
+
"api_jwt_expiry": "240"
|
|
22
|
+
},
|
|
23
|
+
"headers": {
|
|
24
|
+
"content_type": "application/json"
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "local-environment",
|
|
3
|
+
"name": "<%= serviceName %> - Local",
|
|
4
|
+
"values": [
|
|
5
|
+
{
|
|
6
|
+
"key": "base_url",
|
|
7
|
+
"value": "http://localhost:8080/api",
|
|
8
|
+
"type": "default",
|
|
9
|
+
"enabled": true
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"key": "environment",
|
|
13
|
+
"value": "local",
|
|
14
|
+
"type": "default",
|
|
15
|
+
"enabled": true
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"key": "auth_token",
|
|
19
|
+
"value": "temporary_token",
|
|
20
|
+
"type": "default",
|
|
21
|
+
"enabled": true
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"key": "setup_complete",
|
|
25
|
+
"value": "false",
|
|
26
|
+
"type": "default",
|
|
27
|
+
"enabled": true
|
|
28
|
+
}
|
|
29
|
+
],
|
|
30
|
+
"_postman_variable_scope": "environment"
|
|
31
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "staging-environment",
|
|
3
|
+
"name": "<%= serviceName %> - Staging",
|
|
4
|
+
"values": [
|
|
5
|
+
{
|
|
6
|
+
"key": "base_url",
|
|
7
|
+
"value": "https://your-staging-url.freshworks.com/api",
|
|
8
|
+
"type": "default",
|
|
9
|
+
"enabled": true
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"key": "environment",
|
|
13
|
+
"value": "staging",
|
|
14
|
+
"type": "default",
|
|
15
|
+
"enabled": true
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"key": "auth_token",
|
|
19
|
+
"value": "",
|
|
20
|
+
"type": "secret",
|
|
21
|
+
"enabled": true
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"key": "setup_complete",
|
|
25
|
+
"value": "false",
|
|
26
|
+
"type": "default",
|
|
27
|
+
"enabled": true
|
|
28
|
+
}
|
|
29
|
+
],
|
|
30
|
+
"_postman_variable_scope": "environment"
|
|
31
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
node_modules/
|
|
2
|
+
reports/*.html
|
|
3
|
+
reports/*.json
|
|
4
|
+
reports/json/
|
|
5
|
+
*.log
|
|
6
|
+
audit-report.json
|
|
7
|
+
deps-tree.txt
|
|
8
|
+
|
|
9
|
+
# dev-tools staged scripts (machine-local cache; refreshed by `dev-tools test`)
|
|
10
|
+
scripts/.run-all-impl.sh
|
|
11
|
+
scripts/auth/
|
|
12
|
+
scripts/database/
|
|
13
|
+
scripts/infra/
|
|
14
|
+
scripts/lib/
|
|
15
|
+
scripts/report-generators/
|
|
16
|
+
scripts/runners/
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# =============================================================================
|
|
2
|
+
# npm Security Configuration for Postman Tests
|
|
3
|
+
# =============================================================================
|
|
4
|
+
#
|
|
5
|
+
# This file configures npm with security-focused settings to reduce the risk
|
|
6
|
+
# of supply chain attacks (like the tunnel-agent compromise).
|
|
7
|
+
#
|
|
8
|
+
# =============================================================================
|
|
9
|
+
|
|
10
|
+
# CRITICAL: Block postinstall attacks
|
|
11
|
+
# This prevents malicious packages from running scripts during installation
|
|
12
|
+
ignore-scripts=true
|
|
13
|
+
|
|
14
|
+
# Lock dependencies to exact versions
|
|
15
|
+
# Ensures reproducible builds and prevents version drift attacks
|
|
16
|
+
package-lock=true
|
|
17
|
+
save-exact=true
|
|
18
|
+
|
|
19
|
+
# Use official npm registry with SSL
|
|
20
|
+
# Prevents man-in-the-middle attacks and package tampering
|
|
21
|
+
registry=https://registry.npmjs.org/
|
|
22
|
+
strict-ssl=true
|
|
23
|
+
|
|
24
|
+
# Run security audit on install
|
|
25
|
+
# Automatically checks for known CVEs in dependencies
|
|
26
|
+
audit=true
|
|
27
|
+
audit-level=high
|
|
28
|
+
|
|
29
|
+
# Verify package signatures (npm 8.4+)
|
|
30
|
+
# Ensures packages haven't been tampered with after publishing
|
|
31
|
+
audit-signatures=true
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "<%= serviceName %>-postman-tests",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"description": "Postman API tests for <%= serviceName %> - secure configuration with pinned dependencies",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"prepare": "cd .. && husky postman/.husky",
|
|
8
|
+
"test:local": "./scripts/runners/run-tests-local.sh",<% if (includeStaging) { %>
|
|
9
|
+
"test:staging": "./scripts/runners/run-tests-staging.sh",<% } %>
|
|
10
|
+
"audit": "npm audit --audit-level=high",
|
|
11
|
+
"audit:fix": "npm audit fix",
|
|
12
|
+
"audit:json": "npm audit --json > audit-report.json",
|
|
13
|
+
"audit:signatures": "npm audit signatures",
|
|
14
|
+
"deps:check": "npm ls --all 2>/dev/null | grep -E 'tunnel-agent|@postman|got|node-fetch' || echo 'No problematic packages found'",
|
|
15
|
+
"deps:tree": "npm ls --all",
|
|
16
|
+
"deps:outdated": "npm outdated",
|
|
17
|
+
"deps:duplicates": "npm ls --all 2>/dev/null | sort | uniq -d || echo 'No duplicates'",
|
|
18
|
+
"security:check": "npm run audit && npm run audit:signatures && npm run deps:check",
|
|
19
|
+
"security:full": "npm run audit:json && npm run deps:tree > deps-tree.txt && echo 'Reports: audit-report.json, deps-tree.txt'",
|
|
20
|
+
"format": "prettier --write '**/*.json' '!node_modules/**'",
|
|
21
|
+
"format:check": "prettier --check '**/*.json' '!node_modules/**'"
|
|
22
|
+
},
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"newman": "6.2.2",
|
|
25
|
+
"newman-reporter-htmlextra": "1.23.1"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"husky": "9.1.7",
|
|
29
|
+
"prettier": "3.4.2"
|
|
30
|
+
},
|
|
31
|
+
"overrides": {
|
|
32
|
+
"tunnel-agent": "0.6.0",
|
|
33
|
+
"@postman/tunnel-agent": "0.6.3",
|
|
34
|
+
"tough-cookie": "4.1.3",
|
|
35
|
+
"semver": "7.5.4",
|
|
36
|
+
"word-wrap": "1.2.5",
|
|
37
|
+
"xml2js": "0.6.2",
|
|
38
|
+
"http-cache-semantics": "4.1.1",
|
|
39
|
+
"json5": "2.2.3",
|
|
40
|
+
"minimatch": "3.1.2",
|
|
41
|
+
"jose": "4.15.5",
|
|
42
|
+
"node-forge": "1.3.3",
|
|
43
|
+
"qs": "6.15.0",
|
|
44
|
+
"lodash": "4.17.23",
|
|
45
|
+
"ajv": "6.14.0",
|
|
46
|
+
"postman-runtime": {
|
|
47
|
+
"jose": "4.15.5",
|
|
48
|
+
"node-forge": "1.3.3"
|
|
49
|
+
},
|
|
50
|
+
"postman-request": {
|
|
51
|
+
"qs": "6.15.0"
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
"engines": {
|
|
55
|
+
"node": ">=18.0.0",
|
|
56
|
+
"npm": ">=8.4.0"
|
|
57
|
+
},
|
|
58
|
+
"keywords": [
|
|
59
|
+
"postman",
|
|
60
|
+
"api-testing",
|
|
61
|
+
"newman",
|
|
62
|
+
"<%= serviceName %>"
|
|
63
|
+
],
|
|
64
|
+
"author": "Developer Platform Team",
|
|
65
|
+
"license": "UNLICENSED"
|
|
66
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Generated by shiftleft-tools — do not edit.
|
|
3
|
+
#
|
|
4
|
+
# Test orchestration lives in the shiftleft-tools package, not in this repo. This
|
|
5
|
+
# shim stages the latest scripts (gitignored, machine-local) and runs them, so
|
|
6
|
+
# `npm install -g shiftleft-tools` updates behavior with no repo change.
|
|
7
|
+
# All flags pass through unchanged: --env, --skip-unit, --skip-mutation,
|
|
8
|
+
# --skip-postman, --skip-coverage, --skip-report, --no-delay, ...
|
|
9
|
+
|
|
10
|
+
if ! command -v shiftleft >/dev/null 2>&1; then
|
|
11
|
+
echo "error: shiftleft CLI not found." >&2
|
|
12
|
+
echo "Install it with: npm install -g shiftleft-tools --registry=https://nexuscentral.runwayci.com/repository/npm-group/" >&2
|
|
13
|
+
exit 127
|
|
14
|
+
fi
|
|
15
|
+
|
|
16
|
+
exec shiftleft test "$@"
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# =============================================================================
|
|
3
|
+
# SECURE JWT Generator - Uses only bash and openssl (NO Node.js, NO npm)
|
|
4
|
+
# =============================================================================
|
|
5
|
+
#
|
|
6
|
+
# This script replaces generate-jwt.js to eliminate npm supply chain risk.
|
|
7
|
+
# The ISSUER_SECRET never touches any Node.js process.
|
|
8
|
+
#
|
|
9
|
+
# Usage:
|
|
10
|
+
# ISSUER_SECRET=<secret> ./generate-jwt.sh
|
|
11
|
+
#
|
|
12
|
+
# Or with all parameters:
|
|
13
|
+
# ISSUER_SECRET=<secret> \
|
|
14
|
+
# API_ISSUER=mp_collections_manager \
|
|
15
|
+
# API_ACCOUNT_ID=123 \
|
|
16
|
+
# ./generate-jwt.sh
|
|
17
|
+
#
|
|
18
|
+
# =============================================================================
|
|
19
|
+
|
|
20
|
+
set -euo pipefail
|
|
21
|
+
|
|
22
|
+
# Configuration from environment or defaults
|
|
23
|
+
API_ISSUER="${API_ISSUER:-mp_collections_manager}"
|
|
24
|
+
API_ACCOUNT_ID="${API_ACCOUNT_ID:-1}"
|
|
25
|
+
API_ACCOUNT_DOMAIN="${API_ACCOUNT_DOMAIN:-freshworks.com}"
|
|
26
|
+
API_USER_ID="${API_USER_ID:-1}"
|
|
27
|
+
API_PRODUCT="${API_PRODUCT:-freshdesk}"
|
|
28
|
+
API_PRODUCT_ID="${API_PRODUCT_ID:-1}"
|
|
29
|
+
API_USER_ROLE="${API_USER_ROLE:-internal_admin}"
|
|
30
|
+
API_POD="${API_POD:-poduswest2}"
|
|
31
|
+
API_SKUS="${API_SKUS:-estate_pro}"
|
|
32
|
+
SKU="${SKU:-estate_pro}"
|
|
33
|
+
IS_SOURCE_INTERNAL="${IS_SOURCE_INTERNAL:-false}"
|
|
34
|
+
API_JWT_EXPIRY="${API_JWT_EXPIRY:-3600}"
|
|
35
|
+
ISSUER_SECRET="${ISSUER_SECRET:-}"
|
|
36
|
+
|
|
37
|
+
# Validate required secret
|
|
38
|
+
if [ -z "$ISSUER_SECRET" ]; then
|
|
39
|
+
echo "ERROR: ISSUER_SECRET environment variable is required" >&2
|
|
40
|
+
echo "" >&2
|
|
41
|
+
echo "Usage: ISSUER_SECRET=<secret> $0" >&2
|
|
42
|
+
echo "" >&2
|
|
43
|
+
echo "Or set all parameters:" >&2
|
|
44
|
+
echo " ISSUER_SECRET=<secret> \\" >&2
|
|
45
|
+
echo " API_ISSUER=mp_collections_manager \\" >&2
|
|
46
|
+
echo " API_ACCOUNT_ID=123 \\" >&2
|
|
47
|
+
echo " $0" >&2
|
|
48
|
+
exit 1
|
|
49
|
+
fi
|
|
50
|
+
|
|
51
|
+
# =============================================================================
|
|
52
|
+
# Base64URL encode function (URL-safe base64 without padding)
|
|
53
|
+
# This matches the JWT spec: https://tools.ietf.org/html/rfc7519
|
|
54
|
+
# =============================================================================
|
|
55
|
+
base64url_encode() {
|
|
56
|
+
# Read from stdin, base64 encode, convert to URL-safe format, remove padding
|
|
57
|
+
openssl base64 -A | tr '+/' '-_' | tr -d '='
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
# =============================================================================
|
|
61
|
+
# Build JWT
|
|
62
|
+
# =============================================================================
|
|
63
|
+
|
|
64
|
+
# Current timestamp
|
|
65
|
+
NOW=$(date +%s)
|
|
66
|
+
EXP=$((NOW + API_JWT_EXPIRY))
|
|
67
|
+
|
|
68
|
+
# JWT Header (always the same for HS256)
|
|
69
|
+
HEADER='{"alg":"HS256","typ":"JWT"}'
|
|
70
|
+
|
|
71
|
+
# JWT Payload - build dynamically to only include non-empty fields
|
|
72
|
+
PAYLOAD="{"
|
|
73
|
+
PAYLOAD+="\"iss\":\"${API_ISSUER}\""
|
|
74
|
+
|
|
75
|
+
[ -n "$API_ACCOUNT_ID" ] && PAYLOAD+=",\"account_id\":\"${API_ACCOUNT_ID}\""
|
|
76
|
+
[ -n "$API_ACCOUNT_DOMAIN" ] && PAYLOAD+=",\"account_domain\":\"${API_ACCOUNT_DOMAIN}\""
|
|
77
|
+
[ -n "$API_USER_ID" ] && PAYLOAD+=",\"user_id\":\"${API_USER_ID}\""
|
|
78
|
+
[ -n "$API_PRODUCT" ] && PAYLOAD+=",\"product\":\"${API_PRODUCT}\""
|
|
79
|
+
[ -n "$API_PRODUCT_ID" ] && PAYLOAD+=",\"product_id\":\"${API_PRODUCT_ID}\""
|
|
80
|
+
[ -n "$API_USER_ROLE" ] && PAYLOAD+=",\"user_role\":\"${API_USER_ROLE}\""
|
|
81
|
+
[ -n "$API_POD" ] && PAYLOAD+=",\"pod\":\"${API_POD}\""
|
|
82
|
+
[ -n "$API_SKUS" ] && PAYLOAD+=",\"skus\":\"${API_SKUS}\""
|
|
83
|
+
[ -n "$SKU" ] && PAYLOAD+=",\"sku\":\"${SKU}\""
|
|
84
|
+
|
|
85
|
+
# Boolean field
|
|
86
|
+
if [ "$IS_SOURCE_INTERNAL" = "true" ]; then
|
|
87
|
+
PAYLOAD+=",\"is_source_internal\":true"
|
|
88
|
+
fi
|
|
89
|
+
|
|
90
|
+
# Timestamps
|
|
91
|
+
PAYLOAD+=",\"iat\":${NOW}"
|
|
92
|
+
PAYLOAD+=",\"exp\":${EXP}"
|
|
93
|
+
PAYLOAD+="}"
|
|
94
|
+
|
|
95
|
+
# =============================================================================
|
|
96
|
+
# Encode and Sign
|
|
97
|
+
# =============================================================================
|
|
98
|
+
|
|
99
|
+
# Encode header and payload
|
|
100
|
+
HEADER_B64=$(echo -n "$HEADER" | base64url_encode)
|
|
101
|
+
PAYLOAD_B64=$(echo -n "$PAYLOAD" | base64url_encode)
|
|
102
|
+
|
|
103
|
+
# Create signature using HMAC-SHA256
|
|
104
|
+
# This is the secure part - openssl handles the cryptography
|
|
105
|
+
SIGNATURE=$(echo -n "${HEADER_B64}.${PAYLOAD_B64}" | \
|
|
106
|
+
openssl dgst -sha256 -hmac "$ISSUER_SECRET" -binary | \
|
|
107
|
+
base64url_encode)
|
|
108
|
+
|
|
109
|
+
# =============================================================================
|
|
110
|
+
# Output complete JWT
|
|
111
|
+
# =============================================================================
|
|
112
|
+
echo "${HEADER_B64}.${PAYLOAD_B64}.${SIGNATURE}"
|
|
113
|
+
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# Helper script to fetch ISSUER_SECRET from AWS Secrets Manager
|
|
4
|
+
#
|
|
5
|
+
# Usage:
|
|
6
|
+
# ./get-issuer-secret.sh <secret-name> <client> <issuer> [region]
|
|
7
|
+
#
|
|
8
|
+
# Authentication:
|
|
9
|
+
# - In Jenkins/CI: Uses IAM role attached to Kubernetes pod (no profile needed)
|
|
10
|
+
# - Locally: Set AWS_PROFILE environment variable (e.g., export AWS_PROFILE=staging)
|
|
11
|
+
#
|
|
12
|
+
# Example:
|
|
13
|
+
# AWS_PROFILE=staging ./get-issuer-secret.sh staging_stack_secrets developer_platform mp_collections_manager us-west-2
|
|
14
|
+
|
|
15
|
+
set -e
|
|
16
|
+
|
|
17
|
+
# Colors
|
|
18
|
+
GREEN='\033[0;32m'
|
|
19
|
+
RED='\033[0;31m'
|
|
20
|
+
YELLOW='\033[1;33m'
|
|
21
|
+
BLUE='\033[0;34m'
|
|
22
|
+
NC='\033[0m'
|
|
23
|
+
|
|
24
|
+
# Parse arguments
|
|
25
|
+
SECRET_NAME="${1:-}"
|
|
26
|
+
CLIENT="${2:-}"
|
|
27
|
+
ISSUER="${3:-}"
|
|
28
|
+
REGION="${4:-us-east-1}"
|
|
29
|
+
|
|
30
|
+
# Show usage if arguments missing
|
|
31
|
+
if [ -z "$SECRET_NAME" ] || [ -z "$CLIENT" ] || [ -z "$ISSUER" ]; then
|
|
32
|
+
echo -e "${BLUE}╔════════════════════════════════════════════════════════════╗${NC}"
|
|
33
|
+
echo -e "${BLUE}║ Get Issuer Secret from AWS Secrets Manager ║${NC}"
|
|
34
|
+
echo -e "${BLUE}╚════════════════════════════════════════════════════════════╝${NC}"
|
|
35
|
+
echo ""
|
|
36
|
+
echo "Usage: $0 <secret-name> <client> <issuer> [region]"
|
|
37
|
+
echo ""
|
|
38
|
+
echo "Arguments:"
|
|
39
|
+
echo " secret-name AWS Secrets Manager secret name"
|
|
40
|
+
echo " client Client name (e.g., developer_platform)"
|
|
41
|
+
echo " issuer Issuer name (e.g., developer_platform)"
|
|
42
|
+
echo " region AWS region (default: us-east-1)"
|
|
43
|
+
echo ""
|
|
44
|
+
echo "Example:"
|
|
45
|
+
echo " $0 my-secret-name developer_platform developer_platform us-east-1"
|
|
46
|
+
echo ""
|
|
47
|
+
echo "Output:"
|
|
48
|
+
echo " Prints the issuer secret to stdout"
|
|
49
|
+
echo ""
|
|
50
|
+
exit 1
|
|
51
|
+
fi
|
|
52
|
+
|
|
53
|
+
# Check if AWS CLI is installed
|
|
54
|
+
if ! command -v aws &> /dev/null; then
|
|
55
|
+
echo -e "${RED}✗ Error: AWS CLI is not installed${NC}"
|
|
56
|
+
echo ""
|
|
57
|
+
echo "Install AWS CLI:"
|
|
58
|
+
echo " https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html"
|
|
59
|
+
echo ""
|
|
60
|
+
exit 1
|
|
61
|
+
fi
|
|
62
|
+
|
|
63
|
+
# Check if jq is installed
|
|
64
|
+
if ! command -v jq &> /dev/null; then
|
|
65
|
+
echo -e "${RED}✗ Error: jq is not installed${NC}"
|
|
66
|
+
echo ""
|
|
67
|
+
echo "Install jq:"
|
|
68
|
+
echo " brew install jq # macOS"
|
|
69
|
+
echo " apt install jq # Ubuntu/Debian"
|
|
70
|
+
echo ""
|
|
71
|
+
exit 1
|
|
72
|
+
fi
|
|
73
|
+
|
|
74
|
+
# Fetch secret from AWS
|
|
75
|
+
# Disable exit-on-error to capture error details for better diagnostics
|
|
76
|
+
set +e
|
|
77
|
+
|
|
78
|
+
# Use --profile if AWS_PROFILE is set, otherwise use default credentials (IAM role in Jenkins)
|
|
79
|
+
if [ -n "${AWS_PROFILE:-}" ]; then
|
|
80
|
+
SECRET_JSON=$(aws --profile "$AWS_PROFILE" secretsmanager get-secret-value \
|
|
81
|
+
--secret-id "$SECRET_NAME" \
|
|
82
|
+
--region "$REGION" \
|
|
83
|
+
--query SecretString \
|
|
84
|
+
--output text 2>&1)
|
|
85
|
+
else
|
|
86
|
+
SECRET_JSON=$(aws secretsmanager get-secret-value \
|
|
87
|
+
--secret-id "$SECRET_NAME" \
|
|
88
|
+
--region "$REGION" \
|
|
89
|
+
--query SecretString \
|
|
90
|
+
--output text 2>&1)
|
|
91
|
+
fi
|
|
92
|
+
|
|
93
|
+
AWS_EXIT_CODE=$?
|
|
94
|
+
set -e
|
|
95
|
+
|
|
96
|
+
if [ $AWS_EXIT_CODE -ne 0 ]; then
|
|
97
|
+
echo -e "${RED}✗ Error fetching secret from AWS:${NC}" >&2
|
|
98
|
+
echo "$SECRET_JSON" >&2
|
|
99
|
+
echo "" >&2
|
|
100
|
+
|
|
101
|
+
# Check if it's a credentials issue
|
|
102
|
+
if echo "$SECRET_JSON" | grep -q "Unable to locate credentials"; then
|
|
103
|
+
echo -e "${YELLOW}AWS credentials not configured!${NC}" >&2
|
|
104
|
+
echo "" >&2
|
|
105
|
+
echo "Please configure AWS credentials:" >&2
|
|
106
|
+
echo " aws configure" >&2
|
|
107
|
+
echo "" >&2
|
|
108
|
+
echo "You'll need:" >&2
|
|
109
|
+
echo " - AWS Access Key ID" >&2
|
|
110
|
+
echo " - AWS Secret Access Key" >&2
|
|
111
|
+
echo " - Default region: $REGION" >&2
|
|
112
|
+
echo "" >&2
|
|
113
|
+
fi
|
|
114
|
+
exit 1
|
|
115
|
+
fi
|
|
116
|
+
|
|
117
|
+
# For mp-apps API, use jwt_secret_secondary at client level
|
|
118
|
+
# This matches the BaseTest.prototype.mpappsAPISecret implementation
|
|
119
|
+
ISSUER_SECRET=$(echo "$SECRET_JSON" | jq -r ".auth.clients[\"$CLIENT\"].jwt_secret_secondary // empty" 2>/dev/null)
|
|
120
|
+
|
|
121
|
+
# Fall back to primary jwt_secret if secondary not found
|
|
122
|
+
if [ -z "$ISSUER_SECRET" ] || [ "$ISSUER_SECRET" == "null" ]; then
|
|
123
|
+
echo -e "${YELLOW}⚠ Secondary secret not found, falling back to primary client secret${NC}" >&2
|
|
124
|
+
ISSUER_SECRET=$(echo "$SECRET_JSON" | jq -r ".auth.clients[\"$CLIENT\"].jwt_secret // empty" 2>/dev/null)
|
|
125
|
+
fi
|
|
126
|
+
|
|
127
|
+
# Check if secret was found
|
|
128
|
+
if [ -z "$ISSUER_SECRET" ] || [ "$ISSUER_SECRET" == "null" ]; then
|
|
129
|
+
echo -e "${RED}✗ Error: Could not find secret for client '$CLIENT' and issuer '$ISSUER'${NC}" >&2
|
|
130
|
+
echo "" >&2
|
|
131
|
+
echo "Available clients:" >&2
|
|
132
|
+
echo "$SECRET_JSON" | jq -r '.auth.clients | keys[]' 2>/dev/null >&2
|
|
133
|
+
exit 1
|
|
134
|
+
fi
|
|
135
|
+
|
|
136
|
+
# Output the secret (to stdout for easy capture)
|
|
137
|
+
echo -e "${GREEN}✓ Secret found!${NC}" >&2
|
|
138
|
+
echo "" >&2
|
|
139
|
+
echo "$ISSUER_SECRET"
|
|
140
|
+
|