@docupace/pipeline 1.0.118 → 1.0.120
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/.changeset/loud-coins-double.md +5 -0
- package/CHANGELOG.md +6 -0
- package/bin/xray.sh +253 -0
- package/bitbucket-pipelines.yml +2 -0
- package/package.json +1 -1
- package/src/changeset/validate.js +3 -1
- package/src/jfrog/xray-scan.yaml +29 -0
- package/src/next/config.js +1 -1
package/CHANGELOG.md
CHANGED
package/bin/xray.sh
ADDED
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
BASENAME="${BITBUCKET_REPO_SLUG}"
|
|
3
|
+
SLACK_API="https://slack.com/api/chat.postMessage"
|
|
4
|
+
INPUT_TABLE="xray.txt"
|
|
5
|
+
INPUT_JSON="xray.json"
|
|
6
|
+
|
|
7
|
+
# ------------------------------------------------
|
|
8
|
+
# 1️⃣ Parse table to JSON (handle multi-line wrapping)
|
|
9
|
+
# ------------------------------------------------
|
|
10
|
+
|
|
11
|
+
awk '
|
|
12
|
+
BEGIN {
|
|
13
|
+
print "["
|
|
14
|
+
in_vuln = 0
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
# Skip separator and header lines
|
|
18
|
+
/^[┌├└]/ { next }
|
|
19
|
+
/CVE.*SEVERITY.*DIRECT.*DEPENDENCY/ { next }
|
|
20
|
+
|
|
21
|
+
# Process data rows
|
|
22
|
+
/^│/ {
|
|
23
|
+
gsub(/^│ /, "")
|
|
24
|
+
gsub(/ *│ *$/, "")
|
|
25
|
+
|
|
26
|
+
n = split($0, fields, / *│ */)
|
|
27
|
+
|
|
28
|
+
# New vulnerability (has CVE in first column)
|
|
29
|
+
if (n >= 8 && fields[1] ~ /^CVE-/) {
|
|
30
|
+
# Output previous entry if exists
|
|
31
|
+
if (in_vuln) {
|
|
32
|
+
# Parse fixed versions
|
|
33
|
+
fixed_versions = "[]"
|
|
34
|
+
if (saved_fixed_raw != "") {
|
|
35
|
+
gsub(/\] *\[/, "][", saved_fixed_raw)
|
|
36
|
+
gsub(/\[/, "\"", saved_fixed_raw)
|
|
37
|
+
gsub(/\]/, "\"", saved_fixed_raw)
|
|
38
|
+
gsub(/" *"/, "\", \"", saved_fixed_raw)
|
|
39
|
+
gsub(/""/, "\", \"", saved_fixed_raw)
|
|
40
|
+
fixed_versions = "[" saved_fixed_raw "]"
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
component_key = saved_type "://" saved_affected
|
|
44
|
+
direct_component = saved_type "://" saved_direct
|
|
45
|
+
|
|
46
|
+
print " {"
|
|
47
|
+
print " \"vulnerabilities\": [{"
|
|
48
|
+
print " \"severity\": \"" saved_severity "\","
|
|
49
|
+
print " \"cves\": [{\"cve\": \"" saved_cve "\"}],"
|
|
50
|
+
print " \"components\": {"
|
|
51
|
+
print " \"" component_key "\": {"
|
|
52
|
+
print " \"impact_paths\": [[null, {\"component_id\": \"" direct_component "\"}]],"
|
|
53
|
+
print " \"affected_version\": \"" saved_affected_ver "\","
|
|
54
|
+
print " \"fixed_versions\": " fixed_versions
|
|
55
|
+
print " }"
|
|
56
|
+
print " }"
|
|
57
|
+
print " }]"
|
|
58
|
+
print " },"
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
in_vuln = 1
|
|
62
|
+
|
|
63
|
+
# Store new vulnerability
|
|
64
|
+
saved_cve = fields[1]
|
|
65
|
+
saved_severity = fields[2]
|
|
66
|
+
saved_direct = fields[3]
|
|
67
|
+
|
|
68
|
+
saved_affected = fields[5]
|
|
69
|
+
saved_affected_ver = fields[6]
|
|
70
|
+
saved_fixed_raw = fields[7]
|
|
71
|
+
saved_type = tolower(fields[8])
|
|
72
|
+
}
|
|
73
|
+
# Continuation line - append to previous field
|
|
74
|
+
else if (in_vuln && fields[1] == "" && fields[2] == "") {
|
|
75
|
+
if (fields[3] != "") {
|
|
76
|
+
saved_direct = saved_direct fields[3]
|
|
77
|
+
}
|
|
78
|
+
if (fields[5] != "") {
|
|
79
|
+
saved_affected = saved_affected fields[5]
|
|
80
|
+
}
|
|
81
|
+
if (fields[7] != "") {
|
|
82
|
+
saved_fixed_raw = saved_fixed_raw " " fields[7]
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
END {
|
|
88
|
+
if (in_vuln) {
|
|
89
|
+
# Output the last entry
|
|
90
|
+
fixed_versions = "[]"
|
|
91
|
+
if (saved_fixed_raw != "") {
|
|
92
|
+
gsub(/\] *\[/, "][", saved_fixed_raw)
|
|
93
|
+
gsub(/\[/, "\"", saved_fixed_raw)
|
|
94
|
+
gsub(/\]/, "\"", saved_fixed_raw)
|
|
95
|
+
gsub(/" *"/, "\", \"", saved_fixed_raw)
|
|
96
|
+
gsub(/""/, "\", \"", saved_fixed_raw)
|
|
97
|
+
fixed_versions = "[" saved_fixed_raw "]"
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
component_key = saved_type "://" saved_affected
|
|
101
|
+
direct_component = saved_type "://" saved_direct
|
|
102
|
+
|
|
103
|
+
print " {"
|
|
104
|
+
print " \"vulnerabilities\": [{"
|
|
105
|
+
print " \"severity\": \"" saved_severity "\","
|
|
106
|
+
print " \"cves\": [{\"cve\": \"" saved_cve "\"}],"
|
|
107
|
+
print " \"components\": {"
|
|
108
|
+
print " \"" component_key "\": {"
|
|
109
|
+
print " \"impact_paths\": [[null, {\"component_id\": \"" direct_component "\"}]],"
|
|
110
|
+
print " \"affected_version\": \"" saved_affected_ver "\","
|
|
111
|
+
print " \"fixed_versions\": " fixed_versions
|
|
112
|
+
print " }"
|
|
113
|
+
print " }"
|
|
114
|
+
print " }]"
|
|
115
|
+
print " }"
|
|
116
|
+
}
|
|
117
|
+
print "]"
|
|
118
|
+
}
|
|
119
|
+
' "$INPUT_TABLE" > "$INPUT_JSON"
|
|
120
|
+
|
|
121
|
+
# Validate JSON
|
|
122
|
+
if ! jq empty "$INPUT_JSON" 2>/dev/null; then
|
|
123
|
+
echo "❌ Failed to create valid JSON"
|
|
124
|
+
cat "$INPUT_JSON"
|
|
125
|
+
exit 1
|
|
126
|
+
fi
|
|
127
|
+
|
|
128
|
+
echo "✅ JSON created: $INPUT_JSON"
|
|
129
|
+
|
|
130
|
+
# ------------------------------------------------
|
|
131
|
+
# 2️⃣ Severity counts
|
|
132
|
+
# ------------------------------------------------
|
|
133
|
+
read CRITICAL HIGH MEDIUM LOW <<< $(
|
|
134
|
+
jq -r '
|
|
135
|
+
.[]
|
|
136
|
+
| select(.vulnerabilities != null)
|
|
137
|
+
| .vulnerabilities[]
|
|
138
|
+
| .severity
|
|
139
|
+
' "$INPUT_JSON" | awk '
|
|
140
|
+
{ c[$1]++ }
|
|
141
|
+
END {
|
|
142
|
+
print c["Critical"]+0, c["High"]+0, c["Medium"]+0, c["Low"]+0
|
|
143
|
+
}'
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
echo "📊 Counts: Critical=$CRITICAL, High=$HIGH, Medium=$MEDIUM, Low=$LOW"
|
|
147
|
+
|
|
148
|
+
TOTAL=$((CRITICAL + HIGH + MEDIUM + LOW))
|
|
149
|
+
if [[ "$TOTAL" -eq 0 ]]; then
|
|
150
|
+
echo "✅ No vulnerabilities found, skipping Slack notification"
|
|
151
|
+
exit 0
|
|
152
|
+
fi
|
|
153
|
+
|
|
154
|
+
# ------------------------------------------------
|
|
155
|
+
# 3️⃣ Parent Slack message
|
|
156
|
+
# ------------------------------------------------
|
|
157
|
+
PARENT_PAYLOAD=$(jq -cn \
|
|
158
|
+
--arg channel "$XRAY_SLACK_CHANNEL" \
|
|
159
|
+
--arg project "$BASENAME" \
|
|
160
|
+
--argjson critical "$CRITICAL" \
|
|
161
|
+
--argjson high "$HIGH" \
|
|
162
|
+
--argjson medium "$MEDIUM" \
|
|
163
|
+
'{
|
|
164
|
+
channel: $channel,
|
|
165
|
+
blocks: [
|
|
166
|
+
{ type: "header", text: { type: "plain_text", text: "🚨 Dependency Vulnerability Report" } },
|
|
167
|
+
{ type: "section", text: { type: "mrkdwn", text: ("*Project:* " + $project) } },
|
|
168
|
+
{ type: "section", text: { type: "mrkdwn", text: ("🔴 *Critical:* \($critical) 🔥 *High:* \($high) 🎃 *Medium:* \($medium)") } },
|
|
169
|
+
{ type: "context", elements: [{ type: "mrkdwn", text: "Detailed findings are posted in the thread below 👇" }] },
|
|
170
|
+
{ type: "divider" }
|
|
171
|
+
]
|
|
172
|
+
}')
|
|
173
|
+
|
|
174
|
+
PARENT_RESPONSE=$(printf '%s' "$PARENT_PAYLOAD" | curl -s -X POST "$SLACK_API" \
|
|
175
|
+
-H "Authorization: Bearer $XRAY_SLACK_TOKEN" \
|
|
176
|
+
-H "Content-Type: application/json" \
|
|
177
|
+
--data @-)
|
|
178
|
+
|
|
179
|
+
THREAD_TS=$(echo "$PARENT_RESPONSE" | jq -r '.ts')
|
|
180
|
+
|
|
181
|
+
if [[ -z "$THREAD_TS" || "$THREAD_TS" == "null" ]]; then
|
|
182
|
+
echo "❌ Failed to create parent message"
|
|
183
|
+
echo "$PARENT_RESPONSE"
|
|
184
|
+
exit 1
|
|
185
|
+
fi
|
|
186
|
+
|
|
187
|
+
echo "✅ Parent message created. thread_ts=$THREAD_TS"
|
|
188
|
+
|
|
189
|
+
# ------------------------------------------------
|
|
190
|
+
# 4️⃣ Thread entries (sorted, CVE links)
|
|
191
|
+
# ------------------------------------------------
|
|
192
|
+
jq -r '
|
|
193
|
+
def rank:
|
|
194
|
+
if . == "Critical" then 0
|
|
195
|
+
elif . == "High" then 1
|
|
196
|
+
elif . == "Medium" then 2
|
|
197
|
+
elif . == "Low" then 3
|
|
198
|
+
else 9 end;
|
|
199
|
+
|
|
200
|
+
[
|
|
201
|
+
.[]
|
|
202
|
+
| select(.vulnerabilities != null)
|
|
203
|
+
| .vulnerabilities[]
|
|
204
|
+
| . as $v
|
|
205
|
+
| ($v.components | to_entries[]) as $comp
|
|
206
|
+
| ($v.cves // [{cve:"N/A"}])[]
|
|
207
|
+
| {
|
|
208
|
+
sev: $v.severity,
|
|
209
|
+
r: ($v.severity | rank),
|
|
210
|
+
text: "*<https://nvd.nist.gov/vuln/detail/\(.cve)|\(.cve)>*\n*Impacted:* `\(
|
|
211
|
+
$comp.key | sub("^[a-z]+://"; "")
|
|
212
|
+
)` v\(
|
|
213
|
+
$comp.value.affected_version // "N/A"
|
|
214
|
+
)\n*Fixed in:* \(
|
|
215
|
+
($comp.value.fixed_versions // [])
|
|
216
|
+
| join(", ")
|
|
217
|
+
| if . == "" then "N/A" else . end
|
|
218
|
+
)\n*Direct dependencies:*\n• \(
|
|
219
|
+
$comp.value.impact_paths[0][1].component_id
|
|
220
|
+
| sub("^[a-z]+://"; "")
|
|
221
|
+
)"
|
|
222
|
+
}
|
|
223
|
+
]
|
|
224
|
+
| sort_by(.r)
|
|
225
|
+
| .[]
|
|
226
|
+
| [.sev, .text]
|
|
227
|
+
| @tsv
|
|
228
|
+
' "$INPUT_JSON" |
|
|
229
|
+
while IFS=$'\t' read -r SEV TEXT; do
|
|
230
|
+
|
|
231
|
+
EMOJI="👻"
|
|
232
|
+
[[ "$SEV" == "Critical" ]] && EMOJI="🔴"
|
|
233
|
+
[[ "$SEV" == "High" ]] && EMOJI="🔥"
|
|
234
|
+
[[ "$SEV" == "Medium" ]] && EMOJI="🎃"
|
|
235
|
+
|
|
236
|
+
jq -cn \
|
|
237
|
+
--arg channel "$XRAY_SLACK_CHANNEL" \
|
|
238
|
+
--arg ts "$THREAD_TS" \
|
|
239
|
+
--arg body "$EMOJI *$SEV*\n$TEXT" \
|
|
240
|
+
'{
|
|
241
|
+
channel: $channel,
|
|
242
|
+
thread_ts: $ts,
|
|
243
|
+
blocks: [{ type: "section", text: { type: "mrkdwn", text: $body } }]
|
|
244
|
+
}' | curl -s -X POST "$SLACK_API" \
|
|
245
|
+
-H "Authorization: Bearer $XRAY_SLACK_TOKEN" \
|
|
246
|
+
-H "Content-Type: application/json" \
|
|
247
|
+
--data @- > /dev/null
|
|
248
|
+
|
|
249
|
+
sleep 0.1
|
|
250
|
+
|
|
251
|
+
done
|
|
252
|
+
|
|
253
|
+
echo "✅ All vulnerabilities posted to thread"
|
package/bitbucket-pipelines.yml
CHANGED
package/package.json
CHANGED
|
@@ -23,7 +23,9 @@ export async function validateChangesets() {
|
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
if (destBranch.includes('release')) {
|
|
26
|
-
if (
|
|
26
|
+
if (
|
|
27
|
+
releases.some((item) => item.type !== 'patch' && item.type !== 'none')
|
|
28
|
+
) {
|
|
27
29
|
console.error(
|
|
28
30
|
'❌ A changeset must be a patch for change to a release branch',
|
|
29
31
|
);
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
definitions:
|
|
2
|
+
steps:
|
|
3
|
+
- step: &xray
|
|
4
|
+
name: JFrog Xray Dependency Scan (TABLE report only)
|
|
5
|
+
runs-on:
|
|
6
|
+
- self.hosted
|
|
7
|
+
- linux.shell
|
|
8
|
+
- onprem
|
|
9
|
+
script:
|
|
10
|
+
- curl -fL https://install-cli.jfrog.io | sh
|
|
11
|
+
- jf --version
|
|
12
|
+
- jf config add scan1 --url=$JFROG_XRAY_URL --user=$JFROG_XRAY_USER --access-token=$JFROG_XRAY_PASSWORD
|
|
13
|
+
- jf c show
|
|
14
|
+
- mkdir -p xray-report
|
|
15
|
+
- jf audit . --format=table > xray-report/xray-audit.txt
|
|
16
|
+
- jf config remove scan1
|
|
17
|
+
artifacts:
|
|
18
|
+
- xray-report/xray-audit.txt
|
|
19
|
+
- step: &version-step
|
|
20
|
+
runs-on:
|
|
21
|
+
- self.hosted
|
|
22
|
+
- linux.shell
|
|
23
|
+
- onprem
|
|
24
|
+
name: Version, Tag
|
|
25
|
+
caches:
|
|
26
|
+
- gradle
|
|
27
|
+
script:
|
|
28
|
+
- npm config set //registry.npmjs.org/:_authToken $DOCUPACE_NPM_TOKEN
|
|
29
|
+
- eval $PIPELINE_CMD_VERSION_CHANGESET
|
package/src/next/config.js
CHANGED
|
@@ -53,7 +53,7 @@ export const appInfo = {
|
|
|
53
53
|
deployAttributes: ['image.tag'],
|
|
54
54
|
},
|
|
55
55
|
'compliance-edge-graphql-server': {
|
|
56
|
-
repositoryName: '
|
|
56
|
+
repositoryName: 'docupace-graphql-server',
|
|
57
57
|
imageName: 'compliance-edge-graphql-server',
|
|
58
58
|
deployAttributes: [
|
|
59
59
|
'image.tag',
|