@metasession.co/devaudit-cli 0.3.22 → 0.3.23

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@metasession.co/devaudit-cli",
3
- "version": "0.3.22",
3
+ "version": "0.3.23",
4
4
  "description": "DevAudit CLI — installs, syncs, and operates the Metasession SDLC across consumer projects.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -33,7 +33,7 @@
33
33
  },
34
34
  "dependencies": {
35
35
  "@clack/prompts": "^0.8.2",
36
- "@metasession.co/devaudit-plugin-sdk": "^0.3.22",
36
+ "@metasession.co/devaudit-plugin-sdk": "^0.3.23",
37
37
  "ajv": "^8.20.0",
38
38
  "commander": "^12.1.0",
39
39
  "consola": "^3.2.3",
@@ -161,18 +161,38 @@ jobs:
161
161
  run: |
162
162
  # stderr → /dev/null so warnings can't corrupt the JSON (DevAudit #48)
163
163
  npm audit --json > dependency-audit.json 2>/dev/null || true
164
- ACCEPTED="{{ACCEPTED_DEP_RISKS}}"
165
- UNACCEPTED=$(python3 -c "
166
- import json
164
+ # Optional, repository-owned exceptions must be advisory- and
165
+ # range-specific, approved, and unexpired. Missing means none.
166
+ UNACCEPTED=$(python3 - <<'PY' 2>/dev/null || echo "unknown"
167
+ import json, os
168
+ from datetime import date
167
169
  with open('dependency-audit.json') as f:
168
- data = json.load(f)
169
- accepted = set('${ACCEPTED}'.split()) if '${ACCEPTED}' else set()
170
- vulns = data.get('vulnerabilities', {})
171
- issues = [name for name, v in vulns.items()
172
- if v.get('severity') in ('high', 'critical')
173
- and name not in accepted]
174
- print(len(issues))
175
- " 2>/dev/null || echo "unknown")
170
+ audit = json.load(f)
171
+ path = 'compliance/security/accepted-vulnerabilities.json'
172
+ exceptions = []
173
+ if os.path.exists(path):
174
+ with open(path) as f:
175
+ exceptions = json.load(f).get('exceptions', [])
176
+ def accepted(package, finding):
177
+ advisory_ids = {
178
+ item.get('url', '').rsplit('/', 1)[-1]
179
+ for item in finding.get('via', []) if isinstance(item, dict)
180
+ }
181
+ for item in exceptions:
182
+ required = ('advisoryId', 'package', 'vulnerableRange', 'expiresAt', 'approvedBy', 'reason', 'remediationIssue')
183
+ if not all(item.get(key) for key in required):
184
+ continue
185
+ if item['expiresAt'] < date.today().isoformat():
186
+ continue
187
+ if item['package'] == package and item['vulnerableRange'] == finding.get('range') and item['advisoryId'] in advisory_ids:
188
+ print(f"Accepted temporary risk: {item['advisoryId']} ({package}) until {item['expiresAt']}")
189
+ return True
190
+ return False
191
+ findings = audit.get('vulnerabilities', {})
192
+ unresolved = [name for name, finding in findings.items()
193
+ if finding.get('severity') in ('high', 'critical') and not accepted(name, finding)]
194
+ print(len(unresolved))
195
+ PY
176
196
  echo "Unaccepted high/critical: $UNACCEPTED"
177
197
  if [ "$UNACCEPTED" != "0" ] && [ "$UNACCEPTED" != "unknown" ]; then
178
198
  echo "::error::$UNACCEPTED unaccepted high/critical vulnerability(ies)."
@@ -96,6 +96,29 @@ jobs:
96
96
  echo "Nothing to close out (idempotent no-op) — no PR opened."
97
97
  fi
98
98
 
99
+ - name: Report verified manual close-out catch-up
100
+ if: github.event_name == 'workflow_dispatch' && steps.closeout.outputs.changed != 'true' && steps.merge.outputs.merged != 'true'
101
+ run: |
102
+ set -euo pipefail
103
+ REQ="${{ steps.in.outputs.req }}"
104
+ PR_REF="${{ steps.in.outputs.pr }}"
105
+ [ -n "$REQ" ] && [ -n "$PR_REF" ] || { echo "::error::Manual no-op reporting requires release and release_pr."; exit 1; }
106
+ PR_JSON="$(gh pr view "$PR_REF" --repo "$GITHUB_REPOSITORY" --json mergedAt,baseRefName,headRefName,mergeCommit,number,url)"
107
+ HEAD="$(jq -r '.headRefName // empty' <<<"$PR_JSON")"
108
+ BASE="$(jq -r '.baseRefName // empty' <<<"$PR_JSON")"
109
+ MERGED="$(jq -r '.mergedAt // empty' <<<"$PR_JSON")"
110
+ SHA="$(jq -r '.mergeCommit.oid // empty' <<<"$PR_JSON")"
111
+ NUMBER="$(jq -r '.number' <<<"$PR_JSON")"
112
+ URL="$(jq -r '.url' <<<"$PR_JSON")"
113
+ [ -n "$MERGED" ] && [ "$BASE" = develop ] && [ "$HEAD" = "chore/close-out-$REQ" ] && [ -n "$SHA" ] || { echo "::error::release_pr must be a merged develop close-out PR for $REQ."; exit 1; }
114
+ BASE_URL="${DEVAUDIT_BASE_URL:-}"
115
+ [ -n "$BASE_URL" ] && [ -n "$DEVAUDIT_API_KEY" ] || { echo "::error::DevAudit base URL and project API key are required."; exit 1; }
116
+ RELEASE="$(curl --fail-with-body -sS -G "${BASE_URL%/}/api/ci/releases/resolve" -H "Authorization: Bearer ${DEVAUDIT_API_KEY}" --data-urlencode "projectSlug={{PROJECT_SLUG}}" --data-urlencode "versionPrefix=${REQ}")"
117
+ RELEASE_ID="$(jq -r '.latest.id // empty' <<<"$RELEASE")"
118
+ RELEASE_VERSION="$(jq -r '.latest.version // empty' <<<"$RELEASE")"
119
+ [ "$RELEASE_VERSION" = "$REQ" ] && [ -n "$RELEASE_ID" ] || { echo "::error::Unable to resolve $REQ."; exit 1; }
120
+ curl --fail-with-body -sS -X POST "${BASE_URL%/}/api/ci/releases/${RELEASE_ID}/close-out" -H "Authorization: Bearer ${DEVAUDIT_API_KEY}" -H 'Content-Type: application/json' --data "$(jq -nc --arg workflowRunId '${{ github.run_id }}' --arg workflowUrl '${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}' --arg pullRequestUrl "$URL" --arg mergeSha "$SHA" --argjson pullRequestNumber "$NUMBER" '{status:"completed",workflowRunId:$workflowRunId,workflowUrl:$workflowUrl,pullRequestNumber:$pullRequestNumber,pullRequestUrl:$pullRequestUrl,mergeSha:$mergeSha}')"
121
+
99
122
  - name: Close existing sync PRs for same REQ
100
123
  if: steps.closeout.outputs.changed == 'true' || steps.merge.outputs.merged == 'true'
101
124
  run: |
package/sdlc/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@metasession.co/devaudit-sdlc",
3
- "version": "0.3.22",
3
+ "version": "0.3.23",
4
4
  "description": "DevAudit SDLC CLI Engine — cross-agent terminal-driven SDLC orchestrator.",
5
5
  "bin": {
6
6
  "devaudit-sdlc": "./src/bin/devaudit-sdlc.js"