@docupace/pipeline 1.0.123-xray.3 → 1.0.124
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/tenable-scan.sh +3 -12
- package/bitbucket-pipelines.yml +181 -32
- package/package.json +2 -5
- package/src/changeset/canny.js +4 -0
- package/src/changeset/validate.js +0 -10
- package/src/next/util.js +3 -32
- package/pnpm-workspace.yaml +0 -3
- package/src/tenable/args.js +0 -157
- package/src/tenable/args.test.js +0 -226
- package/src/tenable/cli.js +0 -24
- package/src/tenable/docker.js +0 -45
- package/src/tenable/fsops.js +0 -15
- package/src/tenable/scan.js +0 -284
- package/src/tenable/scan.test.js +0 -355
- package/src/veracode/args.js +0 -67
- package/src/veracode/args.test.js +0 -137
- package/src/veracode/cli.js +0 -16
- package/src/veracode/fsops.js +0 -7
- package/src/veracode/scan.js +0 -147
- package/src/veracode/scan.test.js +0 -293
- package/src/veracode/veracode.js +0 -45
- package/src/xray/cli.js +0 -37
- package/src/xray/notify.js +0 -107
- package/src/xray/notify.test.js +0 -161
- package/src/xray/report.js +0 -194
- package/src/xray/report.test.js +0 -278
- package/src/xray/slack.js +0 -14
package/bin/tenable-scan.sh
CHANGED
|
@@ -119,8 +119,8 @@ scan_one() {
|
|
|
119
119
|
"--code-commit-hash" "${COMMIT}"
|
|
120
120
|
"--code-commit-user" "${CODE_USER}"
|
|
121
121
|
"--code-repository-url" "${CODE_REPO_URL}"
|
|
122
|
-
"--min-severity" "$
|
|
123
|
-
"--output-file-formats" "
|
|
122
|
+
"--min-severity" "${MIN_SEVERITY}"
|
|
123
|
+
"--output-file-formats" "Json,Md,JUnit"
|
|
124
124
|
"--output-path" "/tmp/tenable/out"
|
|
125
125
|
"--output-file-name" "${slug}-tenable"
|
|
126
126
|
"--no-color"
|
|
@@ -134,24 +134,15 @@ scan_one() {
|
|
|
134
134
|
)
|
|
135
135
|
fi
|
|
136
136
|
if [[ -n "${TENABLE_FAIL_ON_MIN_SEVERITY:-}" ]]; then
|
|
137
|
-
scan_args+=("--fail-on-min-severity" "$
|
|
137
|
+
scan_args+=("--fail-on-min-severity" "${TENABLE_FAIL_ON_MIN_SEVERITY}")
|
|
138
138
|
fi
|
|
139
139
|
|
|
140
140
|
echo "Running Tenable scan for '${slug}' (min-severity=${MIN_SEVERITY}${TENABLE_FAIL_ON_MIN_SEVERITY:+, fail-on=${TENABLE_FAIL_ON_MIN_SEVERITY}})..."
|
|
141
|
-
# --entrypoint is not optional: Tenable's own GitHub action builds FROM this image
|
|
142
|
-
# solely to replace the entrypoint with a script that runs `tenable container-image
|
|
143
|
-
# scan "$@"`, so the image's own entrypoint is not that binary. Handing the
|
|
144
|
-
# subcommand to whatever it does start fails silently — exit 0, no output, no
|
|
145
|
-
# reports. The binary also takes its output destination from the environment (the
|
|
146
|
-
# action passes no --output-path flag at all), so it is set both ways.
|
|
147
141
|
docker run --rm \
|
|
148
142
|
--workdir /tmp/tenable \
|
|
149
143
|
--volume "${work}:/tmp/tenable" \
|
|
150
144
|
--env TENABLE_API_TOKEN \
|
|
151
145
|
--env TENABLE_API_URL \
|
|
152
|
-
--env "TENABLE_OUTPUT_PATH=/tmp/tenable/out" \
|
|
153
|
-
--env "TENABLE_OUTPUT_FILE_NAME=${slug}-tenable" \
|
|
154
|
-
--entrypoint "${TENABLE_SCANNER_ENTRYPOINT:-tenable}" \
|
|
155
146
|
--pull=always \
|
|
156
147
|
"${SCANNER_IMAGE}" "${scan_args[@]}"
|
|
157
148
|
local scan_exit=$?
|
package/bitbucket-pipelines.yml
CHANGED
|
@@ -8,11 +8,33 @@ definitions:
|
|
|
8
8
|
caches:
|
|
9
9
|
pnpm: ~/.pnpm-store
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
steps:
|
|
12
|
+
- step: &version-step
|
|
13
|
+
runs-on:
|
|
14
|
+
- self.hosted
|
|
15
|
+
- linux.shell
|
|
16
|
+
- onprem
|
|
17
|
+
name: On Merge
|
|
18
|
+
script:
|
|
19
|
+
- npm config set //registry.npmjs.org/:_authToken $DOCUPACE_NPM_TOKEN
|
|
20
|
+
- pnpm install --frozen-lockfile
|
|
21
|
+
- export HOME=/tmp
|
|
22
|
+
- node src/changeset/cli.js version-commit-tag --install-latest
|
|
23
|
+
|
|
24
|
+
# Bitbucket's cross-repo `import: pipeline-shared-scripts:master:<name>`
|
|
25
|
+
# resolves against definitions.pipelines.<name> in the exporting repo, and
|
|
26
|
+
# extracts just that one target subtree — so each entry here must be fully
|
|
27
|
+
# self-contained: a YAML alias pointing at an anchor defined outside of it
|
|
28
|
+
# (in a sibling entry here, or anywhere else in this file) resolves to
|
|
29
|
+
# nothing once extracted in isolation, and the imported step comes back
|
|
30
|
+
# "empty, null, or missing" on the consumer side. Content needed in more
|
|
31
|
+
# than one of these (the Tenable step, the Sonar scan script) is therefore
|
|
32
|
+
# duplicated instead of shared via anchor/alias. pipelines.custom below
|
|
33
|
+
# aliases into these (safe: same-document, not crossing the export
|
|
34
|
+
# boundary) purely so this repo can also run each one manually from its
|
|
35
|
+
# own Pipelines UI.
|
|
14
36
|
pipelines:
|
|
15
|
-
xray-scan: &xray-scan
|
|
37
|
+
xray-scan: &def-xray-scan
|
|
16
38
|
- step:
|
|
17
39
|
name: JFrog Xray Scan (dependencies + Docker image)
|
|
18
40
|
runs-on:
|
|
@@ -235,15 +257,15 @@ definitions:
|
|
|
235
257
|
artifacts:
|
|
236
258
|
- xray-report/**
|
|
237
259
|
|
|
238
|
-
- step:
|
|
260
|
+
- step:
|
|
239
261
|
name: Tenable Cloud Security Scan (container image)
|
|
240
262
|
runs-on:
|
|
241
263
|
- self.hosted
|
|
242
264
|
- linux.shell
|
|
243
265
|
- onprem
|
|
244
266
|
script:
|
|
245
|
-
# bin/tenable-scan.sh lives in this shared repo
|
|
246
|
-
# the YAML across
|
|
267
|
+
# bin/tenable-scan.sh lives in this shared repo, but an import only
|
|
268
|
+
# brings the YAML across — so fetch it the same way the Xray step does.
|
|
247
269
|
- |
|
|
248
270
|
TENABLE_BIN=""
|
|
249
271
|
if [ -f ./bin/tenable-scan.sh ]; then
|
|
@@ -289,8 +311,56 @@ definitions:
|
|
|
289
311
|
|
|
290
312
|
# Runs the Tenable image scan on its own, for repos that want it separately
|
|
291
313
|
# from the Xray scan: import: pipeline-shared-scripts:master:tenable-scan
|
|
292
|
-
tenable-scan:
|
|
293
|
-
- step:
|
|
314
|
+
tenable-scan: &def-tenable-scan
|
|
315
|
+
- step:
|
|
316
|
+
name: Tenable Cloud Security Scan (container image)
|
|
317
|
+
runs-on:
|
|
318
|
+
- self.hosted
|
|
319
|
+
- linux.shell
|
|
320
|
+
- onprem
|
|
321
|
+
script:
|
|
322
|
+
- |
|
|
323
|
+
TENABLE_BIN=""
|
|
324
|
+
if [ -f ./bin/tenable-scan.sh ]; then
|
|
325
|
+
TENABLE_BIN=./bin
|
|
326
|
+
else
|
|
327
|
+
DEST=/tmp/pipeline-shared-scripts
|
|
328
|
+
rm -rf "${DEST}"
|
|
329
|
+
LABELS=""; URLS=""
|
|
330
|
+
add_src() { LABELS="${LABELS}${LABELS:+|}$1"; URLS="${URLS}${URLS:+|}$2"; }
|
|
331
|
+
[ -n "${SCRIPTS_REPO:-}" ] && add_src "SCRIPTS_REPO" "${SCRIPTS_REPO}"
|
|
332
|
+
if [ -n "${BITBUCKET_USERNAME:-}" ] && [ -n "${BITBUCKET_APP_PASSWORD:-}" ]; then
|
|
333
|
+
add_src "BITBUCKET_USERNAME+APP_PASSWORD" "https://${BITBUCKET_USERNAME}:${BITBUCKET_APP_PASSWORD}@bitbucket.org/dp-cloud/pipeline-shared-scripts.git"
|
|
334
|
+
fi
|
|
335
|
+
[ -n "${BITBUCKET_STEP_OAUTH_TOKEN:-}" ] && add_src "step OAuth token" "https://x-token-auth:${BITBUCKET_STEP_OAUTH_TOKEN}@bitbucket.org/dp-cloud/pipeline-shared-scripts.git"
|
|
336
|
+
add_src "ssh" "git@bitbucket.org:dp-cloud/pipeline-shared-scripts.git"
|
|
337
|
+
i=1
|
|
338
|
+
while :; do
|
|
339
|
+
label="$(printf '%s' "${LABELS}" | cut -d'|' -f${i})"
|
|
340
|
+
url="$(printf '%s' "${URLS}" | cut -d'|' -f${i})"
|
|
341
|
+
[ -z "${label}" ] && break
|
|
342
|
+
echo "Fetching shared scripts via ${label}..."
|
|
343
|
+
if GIT_TERMINAL_PROMPT=0 GIT_SSH_COMMAND="ssh -o StrictHostKeyChecking=accept-new -o BatchMode=yes" \
|
|
344
|
+
git clone --depth 1 --quiet "${url}" "${DEST}" 2>/dev/null; then
|
|
345
|
+
TENABLE_BIN="${DEST}/bin"
|
|
346
|
+
echo "Fetched shared scripts via ${label}."
|
|
347
|
+
break
|
|
348
|
+
fi
|
|
349
|
+
rm -rf "${DEST}"
|
|
350
|
+
i=$((i + 1))
|
|
351
|
+
done
|
|
352
|
+
fi
|
|
353
|
+
if [ -z "${TENABLE_BIN}" ]; then
|
|
354
|
+
echo "Could not fetch shared scripts (tried: $(printf '%s' "${LABELS}" | tr '|' ' ')) — skipping Tenable scan."
|
|
355
|
+
exit 0
|
|
356
|
+
fi
|
|
357
|
+
if [ ! -f "${TENABLE_BIN}/tenable-scan.sh" ]; then
|
|
358
|
+
echo "tenable-scan.sh not present in ${TENABLE_BIN} — skipping Tenable scan."
|
|
359
|
+
exit 0
|
|
360
|
+
fi
|
|
361
|
+
bash "${TENABLE_BIN}/tenable-scan.sh"
|
|
362
|
+
artifacts:
|
|
363
|
+
- tenable-report/**
|
|
294
364
|
|
|
295
365
|
# Uploads and scans one or more app targets with Veracode:
|
|
296
366
|
# import: pipeline-shared-scripts:master:veracode-scan
|
|
@@ -300,7 +370,7 @@ definitions:
|
|
|
300
370
|
# (an app profile named <appname> must already exist in the Veracode portal)
|
|
301
371
|
# Optional: VERACODE_BUILD_CMD — eval'd before packaging, so this step is
|
|
302
372
|
# self-contained and needs no separate build step in the consumer pipeline.
|
|
303
|
-
veracode-scan:
|
|
373
|
+
veracode-scan: &def-veracode-scan
|
|
304
374
|
- step:
|
|
305
375
|
name: Veracode Upload and Scan
|
|
306
376
|
runs-on:
|
|
@@ -409,8 +479,8 @@ definitions:
|
|
|
409
479
|
#
|
|
410
480
|
# depth: full — Sonar reads git blame to attribute New Code; a shallow clone
|
|
411
481
|
# only sees the tip commit and silently treats everything as new.
|
|
412
|
-
sonar-scan: &sonar-scan
|
|
413
|
-
- step:
|
|
482
|
+
sonar-scan: &def-sonar-scan
|
|
483
|
+
- step:
|
|
414
484
|
name: SonarQube Scan
|
|
415
485
|
runs-on:
|
|
416
486
|
- self.hosted
|
|
@@ -424,7 +494,7 @@ definitions:
|
|
|
424
494
|
echo "Skipping SonarQube scan: SONAR_TOKEN not set"
|
|
425
495
|
exit 0
|
|
426
496
|
fi
|
|
427
|
-
-
|
|
497
|
+
- |
|
|
428
498
|
export DOCKER_HOST=""
|
|
429
499
|
unset DOCKER_CONTEXT
|
|
430
500
|
SONAR_HOST="${SONAR_HOST_URL:-https://sonarcloud.io}"
|
|
@@ -527,7 +597,7 @@ definitions:
|
|
|
527
597
|
# Required: SONAR_TOKEN (hard-fails if unset and the gate applies to this PR)
|
|
528
598
|
# Optional: same as sonar-scan, plus SONAR_GATE_BRANCH (default "main") and
|
|
529
599
|
# SONAR_QUALITYGATE_TIMEOUT (default 300 seconds).
|
|
530
|
-
sonar-pr-gate:
|
|
600
|
+
sonar-pr-gate: &def-sonar-pr-gate
|
|
531
601
|
- step:
|
|
532
602
|
name: SonarQube Quality Gate
|
|
533
603
|
runs-on:
|
|
@@ -557,27 +627,106 @@ definitions:
|
|
|
557
627
|
fi
|
|
558
628
|
|
|
559
629
|
export SONAR_WAIT_FOR_GATE=true
|
|
560
|
-
-
|
|
630
|
+
- |
|
|
631
|
+
export DOCKER_HOST=""
|
|
632
|
+
unset DOCKER_CONTEXT
|
|
633
|
+
SONAR_HOST="${SONAR_HOST_URL:-https://sonarcloud.io}"
|
|
634
|
+
DEFAULT_BRANCH="${SONAR_DEFAULT_BRANCH:-main}"
|
|
635
|
+
WORKDIR="$(pwd)"
|
|
561
636
|
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
637
|
+
# Bitbucket's built-ins: BITBUCKET_BRANCH / BITBUCKET_PR_ID /
|
|
638
|
+
# BITBUCKET_PR_DESTINATION_BRANCH. An upload with no branch parameter
|
|
639
|
+
# is filed against the project's default branch, so scanning a
|
|
640
|
+
# feature branch without one would silently overwrite that branch's
|
|
641
|
+
# analysis and quality-gate status. Resolved once — every target in
|
|
642
|
+
# this run shares the same branch/PR context.
|
|
643
|
+
BRANCH="${BITBUCKET_BRANCH:-}"
|
|
644
|
+
PR_ID="${BITBUCKET_PR_ID:-}"
|
|
645
|
+
PR_BASE="${BITBUCKET_PR_DESTINATION_BRANCH:-}"
|
|
646
|
+
BRANCH_DOCKER_ARGS=""
|
|
647
|
+
BRANCH_SONAR_ARGS=""
|
|
648
|
+
|
|
649
|
+
if [ -n "${PR_ID}" ]; then
|
|
650
|
+
BRANCH_DOCKER_ARGS="-e PR_ID=${PR_ID}"
|
|
651
|
+
BRANCH_SONAR_ARGS="-Dsonar.pullrequest.key=${PR_ID}"
|
|
652
|
+
[ -n "${BRANCH}" ] && BRANCH_SONAR_ARGS="${BRANCH_SONAR_ARGS} -Dsonar.pullrequest.branch=${BRANCH}"
|
|
653
|
+
[ -n "${PR_BASE}" ] && BRANCH_SONAR_ARGS="${BRANCH_SONAR_ARGS} -Dsonar.pullrequest.base=${PR_BASE}"
|
|
654
|
+
elif [ -n "${BRANCH}" ] && [ "${BRANCH}" != "${DEFAULT_BRANCH}" ]; then
|
|
655
|
+
BRANCH_DOCKER_ARGS="-e BRANCH=${BRANCH}"
|
|
656
|
+
BRANCH_SONAR_ARGS="-Dsonar.branch.name=${BRANCH}"
|
|
657
|
+
elif [ -n "${BITBUCKET_TAG:-}" ] || [ "${BRANCH}" = "${DEFAULT_BRANCH}" ]; then
|
|
658
|
+
echo "Sonar: analysing as the default branch (${DEFAULT_BRANCH})"
|
|
659
|
+
else
|
|
660
|
+
echo "ERROR: cannot tell which branch this analysis belongs to — none of BITBUCKET_PR_ID, BITBUCKET_BRANCH or BITBUCKET_TAG is set." >&2
|
|
661
|
+
echo " Refusing to upload: SonarCloud would file it against '${DEFAULT_BRANCH}' and overwrite that branch's analysis." >&2
|
|
662
|
+
exit 1
|
|
663
|
+
fi
|
|
664
|
+
|
|
665
|
+
if [ "${SONAR_WAIT_FOR_GATE:-false}" = "true" ]; then
|
|
666
|
+
BRANCH_SONAR_ARGS="${BRANCH_SONAR_ARGS} -Dsonar.qualitygate.wait=true -Dsonar.qualitygate.timeout=${SONAR_QUALITYGATE_TIMEOUT:-300}"
|
|
667
|
+
fi
|
|
668
|
+
|
|
669
|
+
# Looks up "${1}_<SLUG>" if set, else prints nothing — POSIX sh has no
|
|
670
|
+
# ${!indirect} expansion, so this shells out through eval instead.
|
|
671
|
+
target_var() {
|
|
672
|
+
eval "printf '%s' \"\${$1_${2}:-}\""
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
# Each target is its own SonarCloud project — like Veracode's per-app
|
|
676
|
+
# profiles, a repo with several components (frontend/backend) needs
|
|
677
|
+
# its own sonar.projectKey per component, not one shared analysis.
|
|
678
|
+
failed=0
|
|
679
|
+
OLD_IFS="${IFS}"
|
|
680
|
+
IFS=';'
|
|
681
|
+
set -- ${SONAR_TARGETS:-default:${SONAR_PROJECT_DIR:-.}}
|
|
682
|
+
IFS="${OLD_IFS}"
|
|
683
|
+
|
|
684
|
+
for target in "$@"; do
|
|
685
|
+
[ -z "${target}" ] && continue
|
|
686
|
+
label="$(printf '%s' "${target}" | cut -d: -f1)"
|
|
687
|
+
project_dir="$(printf '%s' "${target}" | cut -d: -f2)"
|
|
688
|
+
[ -z "${label}" ] && label="default"
|
|
689
|
+
[ -z "${project_dir}" ] && project_dir="."
|
|
690
|
+
slug="$(printf '%s' "${label}" | tr -c 'A-Za-z0-9' '_' | tr 'a-z' 'A-Z')"
|
|
691
|
+
|
|
692
|
+
coverage_cmd="$(target_var SONAR_COVERAGE_CMD "${slug}")"
|
|
693
|
+
[ -z "${coverage_cmd}" ] && coverage_cmd="${SONAR_COVERAGE_CMD:-}"
|
|
694
|
+
coverage_path="$(target_var SONAR_COVERAGE_REPORT_PATH "${slug}")"
|
|
695
|
+
[ -z "${coverage_path}" ] && coverage_path="${SONAR_COVERAGE_REPORT_PATH:-}"
|
|
696
|
+
|
|
697
|
+
if [ -n "${coverage_cmd}" ]; then
|
|
698
|
+
echo "[${label}] Running coverage command..."
|
|
699
|
+
eval "${coverage_cmd}" || echo "WARN: coverage command exited non-zero for '${label}'; checking for the report anyway."
|
|
700
|
+
fi
|
|
701
|
+
if [ -n "${coverage_path}" ] && [ ! -s "${coverage_path}" ]; then
|
|
702
|
+
echo "ERROR: ${coverage_path} missing or empty for target '${label}' — Sonar would report 0% coverage." >&2
|
|
703
|
+
failed=1
|
|
704
|
+
continue
|
|
705
|
+
fi
|
|
706
|
+
|
|
707
|
+
DOCKER_ARGS="run --rm --platform linux/amd64 -e SONAR_HOST_URL=${SONAR_HOST} -e SONAR_TOKEN ${BRANCH_DOCKER_ARGS} -v ${WORKDIR}:/workspace -w /workspace/${project_dir}"
|
|
708
|
+
SONAR_ARGS="sonar-scanner -Dsonar.host.url=${SONAR_HOST} -Dproject.settings=/workspace/${project_dir}/sonar-project.properties ${BRANCH_SONAR_ARGS}"
|
|
709
|
+
|
|
710
|
+
echo "[${label}] Running: docker ${DOCKER_ARGS} sonarsource/sonar-scanner-cli:latest ${SONAR_ARGS}"
|
|
711
|
+
docker ${DOCKER_ARGS} sonarsource/sonar-scanner-cli:latest ${SONAR_ARGS} || {
|
|
712
|
+
failed=1
|
|
713
|
+
echo "ERROR: Sonar scan failed for target '${label}'." >&2
|
|
714
|
+
}
|
|
715
|
+
done
|
|
716
|
+
|
|
717
|
+
[ "${failed}" -ne 0 ] && exit 1
|
|
718
|
+
exit 0
|
|
574
719
|
|
|
575
720
|
pipelines:
|
|
576
|
-
#
|
|
721
|
+
# These simply alias the definitions.pipelines entries above (safe:
|
|
722
|
+
# same-document reuse, not a cross-repo import) so this repo can also run
|
|
723
|
+
# each shared pipeline manually from its own Pipelines UI.
|
|
577
724
|
custom:
|
|
578
|
-
xray-scan: *xray-scan
|
|
579
|
-
tenable-scan:
|
|
580
|
-
|
|
725
|
+
xray-scan: *def-xray-scan
|
|
726
|
+
tenable-scan: *def-tenable-scan
|
|
727
|
+
veracode-scan: *def-veracode-scan
|
|
728
|
+
sonar-scan: *def-sonar-scan
|
|
729
|
+
sonar-pr-gate: *def-sonar-pr-gate
|
|
581
730
|
|
|
582
731
|
pull-requests:
|
|
583
732
|
'**':
|
|
@@ -618,4 +767,4 @@ pipelines:
|
|
|
618
767
|
- pnpm install --frozen-lockfile
|
|
619
768
|
- 'npx eslint . --rule "no-unused-vars: warn" --rule "no-useless-escape: warn" --color'
|
|
620
769
|
- pnpm test
|
|
621
|
-
- pnpm publish --no-git-checks
|
|
770
|
+
- pnpm publish --no-git-checks
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@docupace/pipeline",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.124",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -12,10 +12,7 @@
|
|
|
12
12
|
"dp-next-cli": "src/next/cli.js",
|
|
13
13
|
"dp-auth0-cli": "src/auth0/cli.js",
|
|
14
14
|
"dp-merge-cli": "src/merge/cli.js",
|
|
15
|
-
"dp-micro-cli": "src/micro/cli.js"
|
|
16
|
-
"dp-xray-cli": "src/xray/cli.js",
|
|
17
|
-
"dp-tenable-cli": "src/tenable/cli.js",
|
|
18
|
-
"dp-veracode-cli": "src/veracode/cli.js"
|
|
15
|
+
"dp-micro-cli": "src/micro/cli.js"
|
|
19
16
|
},
|
|
20
17
|
"keywords": [
|
|
21
18
|
"bash"
|
package/src/changeset/canny.js
CHANGED
|
@@ -94,6 +94,10 @@ export async function uploadToCanny(releaseNote, labelIDs) {
|
|
|
94
94
|
|
|
95
95
|
const body = JSON.stringify(bodyPayload);
|
|
96
96
|
|
|
97
|
+
console.log(
|
|
98
|
+
`📤 Posting to Canny: ${JSON.stringify({ ...bodyPayload, apiKey: undefined }, null, 2)}`,
|
|
99
|
+
);
|
|
100
|
+
|
|
97
101
|
// Create a new changelog entry in Canny
|
|
98
102
|
const createResponse = await fetch(
|
|
99
103
|
'https://canny.io/api/v1/entries/create',
|
|
@@ -1,24 +1,14 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import path from 'path';
|
|
3
|
-
import { fileURLToPath } from 'url';
|
|
4
3
|
import { $ } from 'zx';
|
|
5
4
|
|
|
6
5
|
import { setGitInfo } from '../next/util.js';
|
|
7
6
|
import { versionStatus } from './version.js';
|
|
8
7
|
|
|
9
|
-
const PACKAGE_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', '..');
|
|
10
|
-
const PACKAGE_VERSION = JSON.parse(
|
|
11
|
-
fs.readFileSync(path.join(PACKAGE_ROOT, 'package.json'), 'utf8'),
|
|
12
|
-
).version;
|
|
13
|
-
|
|
14
8
|
export async function validateChangesets() {
|
|
15
|
-
console.log(`validateChangesets: @docupace/pipeline version = ${PACKAGE_VERSION}`);
|
|
16
9
|
await setGitInfo();
|
|
17
10
|
const destBranch = process.env.BITBUCKET_PR_DESTINATION_BRANCH;
|
|
18
|
-
console.log(`validateChangesets: destBranch = ${destBranch}`);
|
|
19
|
-
console.log(`validateChangesets: fetching origin ${destBranch}:${destBranch}`);
|
|
20
11
|
await $`git fetch origin ${destBranch}:${destBranch}`;
|
|
21
|
-
console.log('validateChangesets: fetch succeeded');
|
|
22
12
|
let releases;
|
|
23
13
|
|
|
24
14
|
try {
|
package/src/next/util.js
CHANGED
|
@@ -84,41 +84,12 @@ export async function getChangedAppPackages() {
|
|
|
84
84
|
return appPackages;
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
-
function maskCreds(url) {
|
|
88
|
-
return url.replace(/:[^:@/]+@/, ':***@');
|
|
89
|
-
}
|
|
90
|
-
|
|
91
87
|
export async function setGitInfo() {
|
|
92
|
-
console.log(`setGitInfo: SET_GITOPS_USER=${process.env.SET_GITOPS_USER || '(unset)'}`);
|
|
93
88
|
if (process.env.SET_GITOPS_USER) {
|
|
94
|
-
const before = (await $`git remote get-url origin`).stdout.trim();
|
|
95
|
-
console.log(`setGitInfo: origin before = ${maskCreds(before)}`);
|
|
96
|
-
|
|
97
89
|
await $`git config --global user.email "gitops@docupace.com"`;
|
|
98
90
|
await $`git config --global user.name "Gitops"`;
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
// SSH-form origin silently loses its identity at fetch/push time. Use an
|
|
103
|
-
// HTTPS URL with the token embedded instead — insteadOf doesn't touch it.
|
|
104
|
-
const token =
|
|
105
|
-
process.env.BITBUCKET_STEP_OAUTH_TOKEN || process.env.BITBUCKET_TOKEN;
|
|
106
|
-
if (!token) {
|
|
107
|
-
console.log(
|
|
108
|
-
'setGitInfo: no BITBUCKET_STEP_OAUTH_TOKEN/BITBUCKET_TOKEN available, leaving origin as-is',
|
|
109
|
-
);
|
|
110
|
-
} else {
|
|
111
|
-
const repoUrl = `https://x-token-auth:${token}@bitbucket.org/dp-cloud/${process.env.BITBUCKET_REPO_SLUG}.git`;
|
|
112
|
-
console.log(`Setting new origin: ${maskCreds(repoUrl)}`);
|
|
113
|
-
await $`git remote set-url origin ${repoUrl}`;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
const after = (await $`git remote get-url origin`).stdout.trim();
|
|
117
|
-
console.log(`setGitInfo: origin after = ${maskCreds(after)}`);
|
|
118
|
-
} else {
|
|
119
|
-
const current = (await $`git remote get-url origin`).stdout.trim();
|
|
120
|
-
console.log(
|
|
121
|
-
`setGitInfo: SET_GITOPS_USER not set, leaving origin as-is = ${maskCreds(current)}`,
|
|
122
|
-
);
|
|
91
|
+
const repoUrl = `git@bitbucket.org:dp-cloud/${process.env.BITBUCKET_REPO_SLUG}.git`;
|
|
92
|
+
console.log(`Setting new origin: ${repoUrl}`);
|
|
93
|
+
await $`git remote set-url origin ${repoUrl}`;
|
|
123
94
|
}
|
|
124
95
|
}
|
package/pnpm-workspace.yaml
DELETED
package/src/tenable/args.js
DELETED
|
@@ -1,157 +0,0 @@
|
|
|
1
|
-
// Pure parsing/formatting logic for the Tenable scan step, ported from
|
|
2
|
-
// bin/tenable-scan.sh. No fs/docker calls here so it's unit-testable.
|
|
3
|
-
|
|
4
|
-
// Mirrors `tr -c 'A-Za-z0-9._-' '-'`: every character outside that set is
|
|
5
|
-
// replaced with '-' (no squeezing of consecutive replacements).
|
|
6
|
-
export function slugify(label) {
|
|
7
|
-
return label.replace(/[^A-Za-z0-9._-]/g, '-');
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
// Splits `str` on `sep` into exactly `n` fields, folding any extra fields
|
|
11
|
-
// into the last one — matching `read a b c d <<< "$str"` semantics, where a
|
|
12
|
-
// trailing variable absorbs everything left over.
|
|
13
|
-
function splitFixed(str, sep, n) {
|
|
14
|
-
const parts = str.split(sep);
|
|
15
|
-
if (parts.length <= n) {
|
|
16
|
-
return [...parts, ...Array(n - parts.length).fill('')];
|
|
17
|
-
}
|
|
18
|
-
return [...parts.slice(0, n - 1), parts.slice(n - 1).join(sep)];
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
// One "label:auditDir:buildContext:dockerfile" entry from SCAN_TARGETS.
|
|
22
|
-
// auditDir belongs to the Xray step and is ignored here. A target with no
|
|
23
|
-
// build context is returned with skipped: true rather than dropped, so the
|
|
24
|
-
// caller can log why it was skipped (matching the bash script's behavior).
|
|
25
|
-
export function parseScanTargets(scanTargetsStr) {
|
|
26
|
-
return (scanTargetsStr || '')
|
|
27
|
-
.split(';')
|
|
28
|
-
.map((t) => t.trim())
|
|
29
|
-
.filter(Boolean)
|
|
30
|
-
.map((raw) => {
|
|
31
|
-
const [rawLabel, , rawContext, rawDockerfile] = splitFixed(raw, ':', 4);
|
|
32
|
-
const label = rawLabel || 'app';
|
|
33
|
-
const slug = slugify(label);
|
|
34
|
-
if (!rawContext) {
|
|
35
|
-
return { label, slug, buildContext: '', dockerfile: '', skipped: true };
|
|
36
|
-
}
|
|
37
|
-
return {
|
|
38
|
-
label,
|
|
39
|
-
slug,
|
|
40
|
-
buildContext: rawContext,
|
|
41
|
-
dockerfile: rawDockerfile || `${rawContext}/Dockerfile`,
|
|
42
|
-
skipped: false,
|
|
43
|
-
};
|
|
44
|
-
});
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
export function defaultScanTargets(buildContext, dockerfilePath) {
|
|
48
|
-
const ctx = buildContext || '.';
|
|
49
|
-
return `app::${ctx}:${dockerfilePath || `${ctx}/Dockerfile`}`;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export function buildCodeMetadata({
|
|
53
|
-
commit = 'local',
|
|
54
|
-
branch = '',
|
|
55
|
-
prDestinationBranch = '',
|
|
56
|
-
workspace = 'unknown',
|
|
57
|
-
repoSlug = 'unknown',
|
|
58
|
-
gitAuthorName = '',
|
|
59
|
-
} = {}) {
|
|
60
|
-
return {
|
|
61
|
-
shortSha: commit.slice(0, 7),
|
|
62
|
-
codeBranch: branch || prDestinationBranch || 'unknown',
|
|
63
|
-
codeRepoUrl: `https://bitbucket.org/${workspace}/${repoSlug}`,
|
|
64
|
-
codeUser: gitAuthorName || 'bitbucket-pipelines',
|
|
65
|
-
};
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
// The scanner requires --pipeline-run-url as soon as any pipeline flag is
|
|
69
|
-
// supplied, so the trio (id/trigger/url) travels together or not at all —
|
|
70
|
-
// includeInScanArgs tells the caller which case applies.
|
|
71
|
-
export function buildPipelineRunInfo({
|
|
72
|
-
runId = '',
|
|
73
|
-
runTrigger = '',
|
|
74
|
-
runUrl = '',
|
|
75
|
-
prId = '',
|
|
76
|
-
buildNumber = '',
|
|
77
|
-
workspace = 'unknown',
|
|
78
|
-
repoSlug = 'unknown',
|
|
79
|
-
} = {}) {
|
|
80
|
-
const resolvedRunId = runId || buildNumber || '';
|
|
81
|
-
const resolvedTrigger = runTrigger || (prId ? 'pull_request' : '') || 'push';
|
|
82
|
-
const resolvedUrl =
|
|
83
|
-
runUrl ||
|
|
84
|
-
(buildNumber
|
|
85
|
-
? `https://bitbucket.org/${workspace}/${repoSlug}/pipelines/results/${buildNumber}`
|
|
86
|
-
: '');
|
|
87
|
-
|
|
88
|
-
return {
|
|
89
|
-
runId: resolvedRunId,
|
|
90
|
-
runTrigger: resolvedTrigger,
|
|
91
|
-
runUrl: resolvedUrl,
|
|
92
|
-
includeInScanArgs: Boolean(resolvedRunId) && Boolean(resolvedUrl),
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
// The scanner's documented vocabularies are lowercase — severities
|
|
97
|
-
// information|low|medium|high|critical, formats cyclonedx|json|junit|md — and it
|
|
98
|
-
// does not correct a case mismatch or complain about one: it just exports nothing.
|
|
99
|
-
// Callers set these from env vars that read naturally capitalised ("Medium"), so
|
|
100
|
-
// normalise here rather than making every caller remember.
|
|
101
|
-
export function buildScanArgs({
|
|
102
|
-
slug,
|
|
103
|
-
scanTarget,
|
|
104
|
-
codeBranch,
|
|
105
|
-
commit,
|
|
106
|
-
codeUser,
|
|
107
|
-
codeRepoUrl,
|
|
108
|
-
minSeverity,
|
|
109
|
-
runInfo,
|
|
110
|
-
failOnMinSeverity = '',
|
|
111
|
-
}) {
|
|
112
|
-
const args = [
|
|
113
|
-
'container-image',
|
|
114
|
-
'scan',
|
|
115
|
-
'--archive-path',
|
|
116
|
-
'/tmp/tenable/image.tar',
|
|
117
|
-
'--name',
|
|
118
|
-
scanTarget,
|
|
119
|
-
'--code-branch',
|
|
120
|
-
codeBranch,
|
|
121
|
-
'--code-commit-hash',
|
|
122
|
-
commit,
|
|
123
|
-
'--code-commit-user',
|
|
124
|
-
codeUser,
|
|
125
|
-
'--code-repository-url',
|
|
126
|
-
codeRepoUrl,
|
|
127
|
-
'--min-severity',
|
|
128
|
-
String(minSeverity).toLowerCase(),
|
|
129
|
-
'--output-file-formats',
|
|
130
|
-
'json,md,junit',
|
|
131
|
-
'--output-path',
|
|
132
|
-
'/tmp/tenable/out',
|
|
133
|
-
'--output-file-name',
|
|
134
|
-
`${slug}-tenable`,
|
|
135
|
-
'--no-color',
|
|
136
|
-
];
|
|
137
|
-
|
|
138
|
-
if (runInfo?.includeInScanArgs) {
|
|
139
|
-
args.push(
|
|
140
|
-
'--pipeline-run-id',
|
|
141
|
-
runInfo.runId,
|
|
142
|
-
'--pipeline-run-trigger',
|
|
143
|
-
runInfo.runTrigger,
|
|
144
|
-
'--pipeline-run-url',
|
|
145
|
-
runInfo.runUrl,
|
|
146
|
-
);
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
if (failOnMinSeverity) {
|
|
150
|
-
args.push('--fail-on-min-severity', failOnMinSeverity.toLowerCase());
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
return args;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
export const SEPARATOR =
|
|
157
|
-
'==============================================================';
|