@codyswann/lisa 1.46.4 → 1.47.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.
@@ -0,0 +1,269 @@
1
+ # This file is managed by Lisa.
2
+ # Do not edit directly — changes will be overwritten on the next `lisa` run.
3
+ # -----------------------------------------------------------------------------
4
+ # Sentry Issue Creation Workflow
5
+ # -----------------------------------------------------------------------------
6
+ # ⚠️ WARNING: THIS FILE IS AUTO-GENERATED. DO NOT EDIT MANUALLY! ⚠️
7
+ # Any changes may be overwritten by the generation process.
8
+ # This workflow creates a Sentry issue when another workflow fails.
9
+ # It captures details about the failure and creates a standardized issue
10
+ # to help track and resolve CI/CD problems in Sentry.
11
+ #
12
+ # Example usage in another workflow:
13
+ # ```yaml
14
+ # create_sentry_issue_on_failure:
15
+ # if: failure()
16
+ # uses: ./.github/workflows/create-sentry-issue-on-failure.yml
17
+ # with:
18
+ # workflow_name: 'My Workflow'
19
+ # failed_job: 'build_and_test'
20
+ # SENTRY_ORG: ${{ vars.SENTRY_ORG }}
21
+ # SENTRY_PROJECT: ${{ vars.SENTRY_PROJECT }}
22
+ # secrets:
23
+ # SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
24
+ # ```
25
+
26
+ name: 🔴 Sentry Issue on Workflow Failure
27
+
28
+ on:
29
+ workflow_call:
30
+ inputs:
31
+ workflow_name:
32
+ required: true
33
+ type: string
34
+ description: 'Name of the workflow that failed'
35
+ failed_job:
36
+ required: false
37
+ type: string
38
+ description: 'Name of the job that failed (optional)'
39
+ SENTRY_ORG:
40
+ required: true
41
+ type: string
42
+ description: 'Sentry organization slug (e.g., your-company)'
43
+ SENTRY_PROJECT:
44
+ required: true
45
+ type: string
46
+ description: 'Sentry project slug (e.g., serverless-knowledge-platform)'
47
+ environment:
48
+ required: false
49
+ type: string
50
+ default: 'production'
51
+ description: 'Environment where the failure occurred'
52
+ level:
53
+ required: false
54
+ type: string
55
+ default: 'error'
56
+ description: 'Sentry issue level (debug, info, warning, error, fatal)'
57
+ node_version:
58
+ description: 'Node.js version to use'
59
+ required: false
60
+ default: '22.21.1'
61
+ type: string
62
+ package_manager:
63
+ description: 'Package manager to use (npm, yarn, or bun)'
64
+ required: false
65
+ default: 'npm'
66
+ type: string
67
+ working_directory:
68
+ description: 'Directory to run commands in (if not root)'
69
+ required: false
70
+ default: ''
71
+ type: string
72
+ secrets:
73
+ SENTRY_AUTH_TOKEN:
74
+ required: true
75
+ description: 'Sentry Auth Token with project:write scope'
76
+
77
+ # Concurrency is managed by the parent workflow that calls this one
78
+ # This avoids deadlocks between parent and child workflows
79
+
80
+ jobs:
81
+ create_sentry_issue:
82
+ name: 📝 Create Sentry Issue
83
+ runs-on: ubuntu-latest
84
+ timeout-minutes: 5
85
+ steps:
86
+ - name: 📥 Checkout repository
87
+ uses: actions/checkout@v4
88
+
89
+ - name: 🔧 Setup Node.js
90
+ uses: actions/setup-node@v4
91
+ with:
92
+ node-version: ${{ inputs.node_version }}
93
+ cache: ${{ inputs.package_manager != 'bun' && inputs.package_manager || '' }}
94
+
95
+ - name: 🔴 Create Sentry Issue
96
+ id: create_sentry_issue
97
+ run: |
98
+ cd "${{ inputs.working_directory || '.' }}"
99
+
100
+ # Set variables
101
+ WORKFLOW_NAME="${{ inputs.workflow_name }}"
102
+ FAILED_JOB="${{ inputs.failed_job || 'Unknown' }}"
103
+ ENVIRONMENT="${{ inputs.environment }}"
104
+ LEVEL="${{ inputs.level }}"
105
+ RUN_URL="https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"
106
+
107
+ # Get commit message, handling special characters properly
108
+ COMMIT_MESSAGE="${{ github.event.head_commit.message || 'N/A' }}"
109
+
110
+ # Create error message
111
+ if [ "$FAILED_JOB" != "Unknown" ]; then
112
+ ERROR_MESSAGE="CI/CD Workflow Failure: ${WORKFLOW_NAME} - ${FAILED_JOB}"
113
+ else
114
+ ERROR_MESSAGE="CI/CD Workflow Failure: ${WORKFLOW_NAME}"
115
+ fi
116
+
117
+ # Get current timestamp in ISO format
118
+ TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%S.000Z")
119
+
120
+ # Create JSON payload for Sentry API using jq for proper escaping
121
+ PAYLOAD=$(jq -n \
122
+ --arg message "${ERROR_MESSAGE}" \
123
+ --arg level "${LEVEL}" \
124
+ --arg timestamp "${TIMESTAMP}" \
125
+ --arg workflow_name "${WORKFLOW_NAME}" \
126
+ --arg failed_job "${FAILED_JOB}" \
127
+ --arg run_url "${RUN_URL}" \
128
+ --arg repository "${{ github.repository }}" \
129
+ --arg commit "${{ github.sha }}" \
130
+ --arg commit_message "${COMMIT_MESSAGE}" \
131
+ --arg triggered_by "${{ github.actor }}" \
132
+ --arg environment "${ENVIRONMENT}" \
133
+ --arg ref "${{ github.ref }}" \
134
+ --arg run_id "${{ github.run_id }}" \
135
+ '{
136
+ "message": $message,
137
+ "level": $level,
138
+ "timestamp": $timestamp,
139
+ "platform": "other",
140
+ "sdk": {
141
+ "name": "github-actions",
142
+ "version": "1.0.0"
143
+ },
144
+ "logger": "github-actions.ci",
145
+ "environment": $environment,
146
+ "tags": {
147
+ "workflow": $workflow_name,
148
+ "job": $failed_job,
149
+ "repository": $repository,
150
+ "triggered_by": $triggered_by,
151
+ "ref": $ref,
152
+ "source": "github-actions"
153
+ },
154
+ "extra": {
155
+ "workflow_run_url": $run_url,
156
+ "commit_sha": $commit,
157
+ "commit_message": $commit_message,
158
+ "run_id": $run_id,
159
+ "failure_type": "ci_cd_workflow"
160
+ },
161
+ "contexts": {
162
+ "runtime": {
163
+ "name": "github-actions",
164
+ "version": "latest"
165
+ },
166
+ "os": {
167
+ "name": "ubuntu-latest"
168
+ }
169
+ },
170
+ "fingerprint": ["github-actions", $workflow_name, $failed_job]
171
+ }')
172
+
173
+ # Debug: Print the payload to see what'\''s being sent (without sensitive data)
174
+ echo "Debug: Generated payload:"
175
+ echo "$PAYLOAD" | jq 'del(.extra.commit_message)' | head -20
176
+
177
+ # Debug: Print Sentry configuration
178
+ echo "Debug: SENTRY_ORG=${{ inputs.SENTRY_ORG }}"
179
+ echo "Debug: SENTRY_PROJECT=${{ inputs.SENTRY_PROJECT }}"
180
+ echo "Debug: API URL=https://sentry.io/api/0/projects/${{ inputs.SENTRY_ORG }}/${{ inputs.SENTRY_PROJECT }}/store/"
181
+
182
+ # First, let's get the DSN for this project
183
+ DSN_RESPONSE=$(curl -s -w "\n%{http_code}" \
184
+ -H "Authorization: Bearer ${{ secrets.SENTRY_AUTH_TOKEN }}" \
185
+ "https://sentry.io/api/0/projects/${{ inputs.SENTRY_ORG }}/${{ inputs.SENTRY_PROJECT }}/keys/")
186
+
187
+ DSN_STATUS=$(echo "$DSN_RESPONSE" | tail -n1)
188
+ DSN_BODY=$(echo "$DSN_RESPONSE" | head -n -1)
189
+
190
+ if [ "$DSN_STATUS" = "200" ]; then
191
+ # Extract the DSN from the first key
192
+ DSN=$(echo "$DSN_BODY" | jq -r '.[0].dsn.public // empty')
193
+ echo "Debug: Found DSN: ${DSN:0:50}..." # Show first 50 chars for security
194
+
195
+ # Parse DSN to get the project ID and key
196
+ if [[ $DSN =~ https://([^@]+)@([^/]+)/([0-9]+) ]]; then
197
+ SENTRY_KEY="${BASH_REMATCH[1]}"
198
+ SENTRY_HOST="${BASH_REMATCH[2]}"
199
+ PROJECT_ID="${BASH_REMATCH[3]}"
200
+
201
+ # Call Sentry API to create issue using the store endpoint with proper auth
202
+ RESPONSE=$(curl -s -w "\n%{http_code}" \
203
+ -H "X-Sentry-Auth: Sentry sentry_version=7, sentry_key=$SENTRY_KEY" \
204
+ -H "Content-Type: application/json" \
205
+ -X POST \
206
+ --data "${PAYLOAD}" \
207
+ "https://${SENTRY_HOST}/api/${PROJECT_ID}/store/")
208
+ else
209
+ echo "Error: Could not parse DSN"
210
+ exit 1
211
+ fi
212
+ else
213
+ echo "Error: Could not retrieve project DSN. Status: $DSN_STATUS"
214
+ echo "Response: $DSN_BODY"
215
+ exit 1
216
+ fi
217
+
218
+ # Extract HTTP status code from response
219
+ HTTP_STATUS=$(echo "$RESPONSE" | tail -n1)
220
+ RESPONSE_BODY=$(echo "$RESPONSE" | head -n -1)
221
+
222
+ echo "HTTP Status: $HTTP_STATUS"
223
+
224
+ if [ "$HTTP_STATUS" = "200" ] || [ "$HTTP_STATUS" = "201" ]; then
225
+ # Extract issue ID from response
226
+ ISSUE_ID=$(echo "$RESPONSE_BODY" | jq -r '.id // empty')
227
+
228
+ if [ -n "$ISSUE_ID" ] && [ "$ISSUE_ID" != "null" ]; then
229
+ echo "Successfully created Sentry issue: $ISSUE_ID"
230
+ echo "issue_id=$ISSUE_ID" >> $GITHUB_OUTPUT
231
+ echo "issue_url=https://sentry.io/organizations/${{ inputs.SENTRY_ORG }}/issues/?project=${{ inputs.SENTRY_PROJECT }}&query=$ISSUE_ID" >> $GITHUB_OUTPUT
232
+ else
233
+ echo "Issue created but no ID returned. Response:"
234
+ echo "$RESPONSE_BODY" | jq . || echo "$RESPONSE_BODY"
235
+ fi
236
+ else
237
+ echo "Failed to create Sentry issue. API response:"
238
+ echo "Status: $HTTP_STATUS"
239
+ echo "Body: $RESPONSE_BODY"
240
+
241
+ # Provide helpful error messages
242
+ if [ "$HTTP_STATUS" = "400" ]; then
243
+ echo "Error: Bad request. Please check the payload format."
244
+ elif [ "$HTTP_STATUS" = "401" ]; then
245
+ echo "Error: Authentication failed. Please check SENTRY_AUTH_TOKEN."
246
+ elif [ "$HTTP_STATUS" = "403" ]; then
247
+ echo "Error: Permission denied. Please check token permissions (needs project:write)."
248
+ elif [ "$HTTP_STATUS" = "404" ]; then
249
+ echo "Error: Project not found. Please check SENTRY_ORG and SENTRY_PROJECT."
250
+ elif [ "$HTTP_STATUS" = "429" ]; then
251
+ echo "Error: Rate limit exceeded. Please try again later."
252
+ fi
253
+
254
+ exit 1
255
+ fi
256
+
257
+ - name: 📢 Report Issue Creation
258
+ if: steps.create_sentry_issue.outputs.issue_id
259
+ run: |
260
+ cd "${{ inputs.working_directory || '.' }}"
261
+ echo "Created Sentry issue: ${{ steps.create_sentry_issue.outputs.issue_id }}"
262
+ echo "Issue URL: ${{ steps.create_sentry_issue.outputs.issue_url }}"
263
+
264
+ - name: 📢 Report Creation Attempt
265
+ if: steps.create_sentry_issue.conclusion == 'success' && !steps.create_sentry_issue.outputs.issue_id
266
+ run: |
267
+ cd "${{ inputs.working_directory || '.' }}"
268
+ echo "Sentry event was sent successfully, but issue ID was not returned."
269
+ echo "Check your Sentry dashboard: https://sentry.io/organizations/${{ inputs.SENTRY_ORG }}/issues/?project=${{ inputs.SENTRY_PROJECT }}"
@@ -964,30 +964,53 @@ jobs:
964
964
  fi
965
965
  working-directory: ${{ inputs.working_directory || '.' }}
966
966
 
967
- - name: 🔒 Run security audit
967
+ - name: 📋 Load audit exclusions
968
+ id: audit_exclusions
968
969
  run: |
969
- if [ "${{ inputs.package_manager }}" = "npm" ]; then
970
- # Run npm audit in JSON mode and filter out known false positives before failing.
971
- # npm audit lacks a native --ignore flag, so we parse JSON and exclude by GHSA ID.
972
-
973
- # Excluding GHSA-3ppc-4f35-3m26: minimatch ReDoS via repeated wildcards
974
- # Nested dep in aws-cdk-lib; fix requires minimatch v10 (incompatible with ^3.1.2)
975
- # Risk: None - dev-time CDK tooling, no production runtime exposure
976
-
977
- # Excluding GHSA-7r86-cg39-jmmj: minimatch ReDoS via multiple non-adjacent GLOBSTAR segments
978
- # Same transitive dependency chain as GHSA-3ppc-4f35-3m26
979
- # Risk: None - only devDependency tooling, never processes untrusted user input
970
+ GHSA_IDS=""
971
+ CVE_IDS=""
972
+ for config_file in audit.ignore.config.json audit.ignore.local.json; do
973
+ if [ -f "$config_file" ]; then
974
+ FILE_IDS=$(jq -r '.exclusions[].id' "$config_file" 2>/dev/null)
975
+ if [ -n "$FILE_IDS" ]; then
976
+ GHSA_IDS="$GHSA_IDS $FILE_IDS"
977
+ fi
978
+ FILE_CVES=$(jq -r '.exclusions[] | select(.cve != null) | .cve' "$config_file" 2>/dev/null)
979
+ if [ -n "$FILE_CVES" ]; then
980
+ CVE_IDS="$CVE_IDS $FILE_CVES"
981
+ fi
982
+ fi
983
+ done
984
+ GHSA_IDS=$(echo "$GHSA_IDS" | tr ' ' '\n' | sort -u | grep -v '^$' | tr '\n' ' ')
985
+ CVE_IDS=$(echo "$CVE_IDS" | tr ' ' '\n' | sort -u | grep -v '^$' | tr '\n' ' ')
986
+ echo "ghsa_ids=$GHSA_IDS" >> $GITHUB_OUTPUT
987
+ echo "cve_ids=$CVE_IDS" >> $GITHUB_OUTPUT
988
+ echo "Loaded GHSA exclusions: $GHSA_IDS"
989
+ echo "Loaded CVE exclusions: $CVE_IDS"
990
+ working-directory: ${{ inputs.working_directory || '.' }}
980
991
 
981
- # Excluding GHSA-23c5-xmqv-rm74: minimatch ReDoS via nested *() extglobs
982
- # Same transitive dependency chain as GHSA-3ppc-4f35-3m26
983
- # Risk: None - only devDependency tooling, never processes untrusted user input
992
+ - name: 🔒 Run security audit
993
+ run: |
994
+ GHSA_IDS="${{ steps.audit_exclusions.outputs.ghsa_ids }}"
995
+ CVE_IDS="${{ steps.audit_exclusions.outputs.cve_ids }}"
984
996
 
985
- # Excluding GHSA-2g4f-4pwh-qvx6: ajv ReDoS with $data option
986
- # Nested dep in aws-cdk-lib and eslint; no fix available via npm
987
- # Risk: Low - $data option not used in this application
997
+ if [ "${{ inputs.package_manager }}" = "npm" ]; then
998
+ # Build jq exclusion filter for npm audit GHSA IDs
999
+ NPM_EXCLUDE_FILTER=""
1000
+ for _id in $GHSA_IDS; do
1001
+ if [ -n "$NPM_EXCLUDE_FILTER" ]; then
1002
+ NPM_EXCLUDE_FILTER="$NPM_EXCLUDE_FILTER or . == \"$_id\""
1003
+ else
1004
+ NPM_EXCLUDE_FILTER=". == \"$_id\""
1005
+ fi
1006
+ done
988
1007
 
989
1008
  AUDIT_JSON=$(npm audit --production --json 2>/dev/null || true)
990
- UNFIXED_HIGH=$(echo "$AUDIT_JSON" | jq '[.vulnerabilities | to_entries[] | select(.value.severity == "high" or .value.severity == "critical") | .value.via[] | select(type == "object") | .url | ltrimstr("https://github.com/advisories/")] | unique | map(select(. == "GHSA-3ppc-4f35-3m26" or . == "GHSA-7r86-cg39-jmmj" or . == "GHSA-23c5-xmqv-rm74" or . == "GHSA-2g4f-4pwh-qvx6" | not)) | length')
1009
+ if [ -n "$NPM_EXCLUDE_FILTER" ]; then
1010
+ UNFIXED_HIGH=$(echo "$AUDIT_JSON" | jq "[.vulnerabilities | to_entries[] | select(.value.severity == \"high\" or .value.severity == \"critical\") | .value.via[] | select(type == \"object\") | .url | ltrimstr(\"https://github.com/advisories/\")] | unique | map(select($NPM_EXCLUDE_FILTER | not)) | length")
1011
+ else
1012
+ UNFIXED_HIGH=$(echo "$AUDIT_JSON" | jq '[.vulnerabilities | to_entries[] | select(.value.severity == "high" or .value.severity == "critical") | .value.via[] | select(type == "object") | .url | ltrimstr("https://github.com/advisories/")] | unique | length')
1013
+ fi
991
1014
  if [ "$UNFIXED_HIGH" -gt 0 ]; then
992
1015
  echo "::warning::Found high or critical vulnerabilities (after excluding known false positives)"
993
1016
  npm audit --production --audit-level=high || true
@@ -995,26 +1018,42 @@ jobs:
995
1018
  fi
996
1019
  echo "::notice::No high or critical vulnerabilities found (excluding known false positives)"
997
1020
  elif [ "${{ inputs.package_manager }}" = "yarn" ]; then
998
- # Yarn audit outputs newline-delimited JSON, so we need to parse each line
999
-
1000
- # Excluding GHSA-5j98-mcp5-4vw2 (CVE-2025-64756): glob CLI command injection
1001
- # This vulnerability only affects the glob CLI (--cmd flag), not library usage
1002
- # We only use glob as a library through Babel and other tools - never invoke CLI
1003
- # Risk: None - vulnerable code path is not executed in our application
1004
-
1005
- # Excluding GHSA-w532-jxjh-hjhj (CVE-2025-29907): jsPDF ReDoS in addImage
1006
- # Excluding GHSA-8mvj-3j78-4qmw (CVE-2025-57810): jsPDF DoS in addImage
1007
- # These require user control of addImage input with malicious data
1008
- # Our usage is controlled and doesn't expose this attack vector
1009
- # Tracked for upgrade in separate security remediation ticket
1010
-
1011
- # Excluding GHSA-36jr-mh4h-2g58: d3-color ReDoS
1012
- # Transitive dependency through react-native-svg-charts (unmaintained)
1013
- # Replacement charting library evaluation in progress
1014
- # Risk: Low - color parsing is not user-controlled in our implementation
1015
-
1016
- # Filter by both GHSA ID and CVE ID for robustness
1017
- yarn audit --groups dependencies --json | jq -r 'select(.type == "auditAdvisory") | select(.data.advisory.severity == "high" or .data.advisory.severity == "critical") | select((.data.advisory.github_advisory_id == "GHSA-5j98-mcp5-4vw2" or .data.advisory.github_advisory_id == "GHSA-w532-jxjh-hjhj" or .data.advisory.github_advisory_id == "GHSA-8mvj-3j78-4qmw" or .data.advisory.github_advisory_id == "GHSA-36jr-mh4h-2g58" or (.data.advisory.cves | any(. == "CVE-2025-64756" or . == "CVE-2025-29907" or . == "CVE-2025-57810"))) | not) | .data.advisory' > high_vulns.json
1021
+ # Build jq filter for GHSA IDs
1022
+ GHSA_FILTER=""
1023
+ for _id in $GHSA_IDS; do
1024
+ if [ -n "$GHSA_FILTER" ]; then
1025
+ GHSA_FILTER="$GHSA_FILTER or .data.advisory.github_advisory_id == \"$_id\""
1026
+ else
1027
+ GHSA_FILTER=".data.advisory.github_advisory_id == \"$_id\""
1028
+ fi
1029
+ done
1030
+
1031
+ # Build jq filter for CVE IDs
1032
+ CVE_FILTER=""
1033
+ for _cve in $CVE_IDS; do
1034
+ if [ -n "$CVE_FILTER" ]; then
1035
+ CVE_FILTER="$CVE_FILTER or . == \"$_cve\""
1036
+ else
1037
+ CVE_FILTER=". == \"$_cve\""
1038
+ fi
1039
+ done
1040
+
1041
+ # Combine GHSA and CVE filters
1042
+ COMBINED_FILTER=""
1043
+ if [ -n "$GHSA_FILTER" ] && [ -n "$CVE_FILTER" ]; then
1044
+ COMBINED_FILTER="($GHSA_FILTER or (.data.advisory.cves | any($CVE_FILTER)))"
1045
+ elif [ -n "$GHSA_FILTER" ]; then
1046
+ COMBINED_FILTER="($GHSA_FILTER)"
1047
+ elif [ -n "$CVE_FILTER" ]; then
1048
+ COMBINED_FILTER="((.data.advisory.cves | any($CVE_FILTER)))"
1049
+ fi
1050
+
1051
+ if [ -n "$COMBINED_FILTER" ]; then
1052
+ yarn audit --groups dependencies --json | jq -r "select(.type == \"auditAdvisory\") | select(.data.advisory.severity == \"high\" or .data.advisory.severity == \"critical\") | select(($COMBINED_FILTER) | not) | .data.advisory" > high_vulns.json
1053
+ else
1054
+ yarn audit --groups dependencies --json | jq -r 'select(.type == "auditAdvisory") | select(.data.advisory.severity == "high" or .data.advisory.severity == "critical") | .data.advisory' > high_vulns.json
1055
+ fi
1056
+
1018
1057
  if [ -s high_vulns.json ]; then
1019
1058
  echo "::error::Found high or critical vulnerabilities:"
1020
1059
  cat high_vulns.json
@@ -1023,64 +1062,13 @@ jobs:
1023
1062
  echo "::notice::No high or critical vulnerabilities found (excluding known false positives)"
1024
1063
  fi
1025
1064
  elif [ "${{ inputs.package_manager }}" = "bun" ]; then
1026
- # Excluding GHSA-5j98-mcp5-4vw2 (CVE-2025-64756): glob CLI command injection
1027
- # This vulnerability only affects the glob CLI (--cmd flag), not library usage
1028
- # We only use glob as a library through Babel and other tools - never invoke CLI
1029
-
1030
- # Excluding GHSA-8qq5-rm4j-mr97: node-tar path sanitization vulnerability
1031
- # Nested dependency in @expo/cli - bun resolves to patched version but audit still flags it
1032
- # Risk: Low - only affects tar extraction with malicious filenames, not our use case
1033
-
1034
- # Excluding GHSA-37qj-frw5-hhjh: fast-xml-parser RangeError DoS with numeric entities
1035
- # Transitive dependency via @react-native-community/cli (Android/iOS build tooling)
1036
- # Parent packages pin ^4.4.1; fix requires major version 5.x (incompatible)
1037
- # Risk: None - CLI build tool, not a production runtime dependency
1038
-
1039
- # Excluding GHSA-3ppc-4f35-3m26: minimatch ReDoS via repeated wildcards
1040
- # Transitive dependency in devDependencies (eslint, jest, nodemon, ts-morph, etc.)
1041
- # Fix requires minimatch v10 which changes export shape (object vs function),
1042
- # breaking test-exclude (used by Jest coverage). No production code path is affected.
1043
- # Risk: None - only devDependency tooling, never processes untrusted user input
1044
-
1045
- # Excluding GHSA-jmr7-xgp7-cmfj: fast-xml-parser DoS through entity expansion in DOCTYPE
1046
- # Transitive dependency via AWS SDK (@aws-sdk/xml-builder) and snowflake-sdk
1047
- # Resolution to >=5.3.6 set in package.json but bun audit still flags intermediate ranges
1048
- # Risk: Low - XML parsing of untrusted DOCTYPE content not in our code paths
1049
-
1050
- # Excluding GHSA-m7jm-9gc2-mpf2: fast-xml-parser entity encoding bypass via regex injection
1051
- # Same transitive path as GHSA-jmr7-xgp7-cmfj (AWS SDK, snowflake-sdk)
1052
- # Resolution to >=5.3.6 set in package.json but bun audit still flags intermediate ranges
1053
- # Risk: Low - no untrusted XML with DOCTYPE entity names processed
1054
-
1055
- # Excluding GHSA-r6q2-hw4h-h46w: node-tar race condition via Unicode Ligature Collisions on macOS APFS
1056
- # Transitive via @nestjs/apollo > @apollo/gateway > make-fetch-happen > cacache > tar
1057
- # Resolution to ^7.5.8 set in package.json but bun audit still flags intermediate ranges
1058
- # Risk: None - tar extraction not used in production runtime
1059
-
1060
- # Excluding GHSA-34x7-hfp2-rc4v: node-tar arbitrary file creation via hardlink path traversal
1061
- # Same transitive path as GHSA-r6q2-hw4h-h46w
1062
- # Risk: None - tar extraction not used in production runtime
1063
-
1064
- # Excluding GHSA-83g3-92jg-28cx: node-tar arbitrary file read/write via hardlink target escape
1065
- # Same transitive path as GHSA-r6q2-hw4h-h46w
1066
- # Risk: None - tar extraction not used in production runtime
1067
-
1068
- # Excluding GHSA-3h5v-q93c-6h6q: ws DoS when handling request with many HTTP headers
1069
- # Transitive via @nestjs/graphql, graphql-ws, openai, serverless-offline, serverless-esbuild
1070
- # Resolution to ^8.17.1 set in package.json but bun audit still flags intermediate ranges
1071
- # Risk: Low - WebSocket servers behind API Gateway which limits headers
1072
-
1073
- # Excluding GHSA-7r86-cg39-jmmj: minimatch ReDoS via multiple non-adjacent GLOBSTAR segments
1074
- # Same transitive dependency chain as GHSA-3ppc-4f35-3m26 (eslint, jest, ts-morph, etc.)
1075
- # Fix requires minimatch >=3.1.3 but bun cannot override transitive dependency version ranges
1076
- # Risk: None - only devDependency tooling, never processes untrusted user input
1077
-
1078
- # Excluding GHSA-23c5-xmqv-rm74: minimatch ReDoS via nested *() extglobs
1079
- # Same transitive dependency chain as GHSA-3ppc-4f35-3m26 (eslint, jest, ts-morph, etc.)
1080
- # Fix requires minimatch >=3.1.3 but bun cannot override transitive dependency version ranges
1081
- # Risk: None - only devDependency tooling, never processes untrusted user input
1082
-
1083
- if ! bun audit --audit-level=high --ignore GHSA-5j98-mcp5-4vw2 --ignore GHSA-8qq5-rm4j-mr97 --ignore GHSA-37qj-frw5-hhjh --ignore GHSA-3ppc-4f35-3m26 --ignore GHSA-jmr7-xgp7-cmfj --ignore GHSA-m7jm-9gc2-mpf2 --ignore GHSA-r6q2-hw4h-h46w --ignore GHSA-34x7-hfp2-rc4v --ignore GHSA-83g3-92jg-28cx --ignore GHSA-3h5v-q93c-6h6q --ignore GHSA-7r86-cg39-jmmj --ignore GHSA-23c5-xmqv-rm74; then
1065
+ # Build --ignore flags dynamically from exclusion list
1066
+ BUN_IGNORE_FLAGS=""
1067
+ for _id in $GHSA_IDS; do
1068
+ BUN_IGNORE_FLAGS="$BUN_IGNORE_FLAGS --ignore $_id"
1069
+ done
1070
+
1071
+ if ! bun audit --audit-level=high $BUN_IGNORE_FLAGS; then
1084
1072
  echo "::warning::Found high or critical vulnerabilities"
1085
1073
  exit 1
1086
1074
  fi
@@ -0,0 +1,87 @@
1
+ {
2
+ "exclusions": [
3
+ {
4
+ "id": "GHSA-5j98-mcp5-4vw2",
5
+ "cve": "CVE-2025-64756",
6
+ "package": "glob",
7
+ "reason": "CLI command injection — only affects glob CLI --cmd flag, not library usage"
8
+ },
9
+ {
10
+ "id": "GHSA-8qq5-rm4j-mr97",
11
+ "package": "node-tar",
12
+ "reason": "Path sanitization vulnerability — nested in @expo/cli, tar extraction not in our code path"
13
+ },
14
+ {
15
+ "id": "GHSA-37qj-frw5-hhjh",
16
+ "package": "fast-xml-parser",
17
+ "reason": "RangeError DoS with numeric entities — transitive via React Native CLI, build tool only"
18
+ },
19
+ {
20
+ "id": "GHSA-3ppc-4f35-3m26",
21
+ "package": "minimatch",
22
+ "reason": "ReDoS via repeated wildcards — devDeps only, fix requires breaking minimatch v10"
23
+ },
24
+ {
25
+ "id": "GHSA-7r86-cg39-jmmj",
26
+ "package": "minimatch",
27
+ "reason": "ReDoS via multiple non-adjacent GLOBSTAR segments — devDeps only, fix requires minimatch >=3.1.3"
28
+ },
29
+ {
30
+ "id": "GHSA-23c5-xmqv-rm74",
31
+ "package": "minimatch",
32
+ "reason": "ReDoS via nested *() extglobs — devDeps only, fix requires minimatch >=3.1.3"
33
+ },
34
+ {
35
+ "id": "GHSA-2g4f-4pwh-qvx6",
36
+ "package": "ajv",
37
+ "reason": "ReDoS with $data option — $data option not used, nested in aws-cdk-lib/eslint"
38
+ },
39
+ {
40
+ "id": "GHSA-jmr7-xgp7-cmfj",
41
+ "package": "fast-xml-parser",
42
+ "reason": "DoS through entity expansion in DOCTYPE — transitive via AWS SDK, no untrusted XML parsing"
43
+ },
44
+ {
45
+ "id": "GHSA-m7jm-9gc2-mpf2",
46
+ "package": "fast-xml-parser",
47
+ "reason": "Entity encoding bypass via regex injection — same path as GHSA-jmr7-xgp7-cmfj"
48
+ },
49
+ {
50
+ "id": "GHSA-r6q2-hw4h-h46w",
51
+ "package": "node-tar",
52
+ "reason": "Race condition via Unicode Ligature Collisions on macOS APFS — transitive via NestJS/Apollo, tar not used in production"
53
+ },
54
+ {
55
+ "id": "GHSA-34x7-hfp2-rc4v",
56
+ "package": "node-tar",
57
+ "reason": "Arbitrary file creation via hardlink path traversal — same path as GHSA-r6q2-hw4h-h46w"
58
+ },
59
+ {
60
+ "id": "GHSA-83g3-92jg-28cx",
61
+ "package": "node-tar",
62
+ "reason": "Arbitrary file read/write via hardlink target escape — same path as GHSA-r6q2-hw4h-h46w"
63
+ },
64
+ {
65
+ "id": "GHSA-3h5v-q93c-6h6q",
66
+ "package": "ws",
67
+ "reason": "DoS via many HTTP headers — WebSocket servers behind API Gateway which limits headers"
68
+ },
69
+ {
70
+ "id": "GHSA-w532-jxjh-hjhj",
71
+ "cve": "CVE-2025-29907",
72
+ "package": "jsPDF",
73
+ "reason": "ReDoS in addImage — controlled usage only, no user-controlled input to addImage"
74
+ },
75
+ {
76
+ "id": "GHSA-8mvj-3j78-4qmw",
77
+ "cve": "CVE-2025-57810",
78
+ "package": "jsPDF",
79
+ "reason": "DoS in addImage — controlled usage only, no user-controlled input to addImage"
80
+ },
81
+ {
82
+ "id": "GHSA-36jr-mh4h-2g58",
83
+ "package": "d3-color",
84
+ "reason": "ReDoS — transitive via react-native-svg-charts, color parsing not user-controlled"
85
+ }
86
+ ]
87
+ }
@@ -70,6 +70,9 @@
70
70
  "scripts/**",
71
71
 
72
72
  "lib/**/*.js",
73
- "cdk.out/**"
73
+ "cdk.out/**",
74
+
75
+ "audit.ignore.config.json",
76
+ "audit.ignore.local.json"
74
77
  ]
75
78
  }
@@ -0,0 +1,3 @@
1
+ {
2
+ "exclusions": []
3
+ }