@windyroad/risk-scorer 0.19.13-preview.1284 → 0.19.13

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.
@@ -159,6 +159,7 @@ EOF
159
159
  # ---------- Surface detection ----------
160
160
  SURFACE=""
161
161
  DRAFT=""
162
+ BODY_FILE_ERROR=0
162
163
 
163
164
  case "$TOOL_NAME" in
164
165
  Bash)
@@ -169,6 +170,15 @@ try:
169
170
  except Exception:
170
171
  print('')
171
172
  " 2>/dev/null || echo "")
173
+ TOOL_CWD=$(printf '%s' "$INPUT" | python3 -c "
174
+ import sys, json
175
+ try:
176
+ event = json.load(sys.stdin)
177
+ tool = event.get('tool_input', {})
178
+ print(tool.get('cwd') or tool.get('workdir') or event.get('cwd') or '')
179
+ except Exception:
180
+ print('')
181
+ " 2>/dev/null || echo "")
172
182
 
173
183
  # Surface match — most-specific first.
174
184
  if echo "$COMMAND" | grep -qE '(^|;|&&|\|\|)\s*gh issue create(\s|$)'; then
@@ -230,13 +240,78 @@ except Exception:
230
240
  # Then --body / --field for the gh + npm + security-advisories surfaces.
231
241
  # Then -m / --message for git commit (single-line literal forms).
232
242
  #
233
- # When absent (npm publish, --body-file, editor flow already filtered),
234
- # DRAFT="" is acceptable: the agent will be invoked with command
235
- # context and read whatever body source the call uses.
236
- DRAFT=$(printf '%s' "$COMMAND" | python3 -c "
237
- import sys, re
243
+ # When absent (npm publish, editor flow already filtered), DRAFT=""
244
+ # is acceptable. A named body file must be readable before review.
245
+ if ! DRAFT=$(printf '%s' "$COMMAND" | python3 -c "
246
+ import sys, re, os, shlex
238
247
  cmd = sys.stdin.read()
239
248
  surface = sys.argv[1]
249
+ tool_cwd = sys.argv[2]
250
+ if surface.startswith('gh-'):
251
+ try:
252
+ lexer = shlex.shlex(cmd, posix=True, punctuation_chars=';&|')
253
+ lexer.whitespace_split = True
254
+ lexer.commenters = ''
255
+ args = list(lexer)
256
+ except ValueError:
257
+ if '--body-file' in cmd or ' -F' in cmd or ' -b' in cmd:
258
+ raise SystemExit(2)
259
+ args = []
260
+ try:
261
+ base = os.path.abspath(tool_cwd) if os.path.isabs(tool_cwd) else ''
262
+ if len(args) >= 4 and args[0] == 'cd' and args[2] == '&&':
263
+ if os.path.isabs(args[1]):
264
+ base = os.path.abspath(args[1])
265
+ elif base:
266
+ base = os.path.abspath(os.path.join(base, args[1]))
267
+ args = args[3:]
268
+ # Skip known option values so a quoted title/body beginning with -F
269
+ # cannot be mistaken for a second body source.
270
+ valued = {'--title', '-t', '--template', '-T', '--repo', '-R',
271
+ '--assignee', '-a', '--label', '-l', '--reviewer', '-r',
272
+ '--milestone', '-m', '--project', '-p', '--base', '-B', '--head', '-H'}
273
+ sources = []
274
+ i = 3
275
+ while i < len(args):
276
+ arg = args[i]
277
+ if arg and all(ch in ';&|' for ch in arg):
278
+ raise ValueError('ambiguous command')
279
+ if arg in ('--body-file', '-F', '--body', '-b'):
280
+ sources.append(('file' if arg in ('--body-file', '-F') else 'body',
281
+ args[i + 1] if i + 1 < len(args) else '', arg))
282
+ i += 2
283
+ elif arg.startswith('--body-file=') or arg.startswith('--body='):
284
+ sources.append(('file' if arg.startswith('--body-file=') else 'body',
285
+ arg.split('=', 1)[1], arg.split('=', 1)[0]))
286
+ i += 1
287
+ elif arg.startswith('-F') or arg.startswith('-b'):
288
+ sources.append(('file' if arg.startswith('-F') else 'body',
289
+ arg[3:] if arg[2:3] == '=' else arg[2:], arg[:2]))
290
+ i += 1
291
+ elif arg in valued:
292
+ i += 2
293
+ else:
294
+ i += 1
295
+ files = [value for kind, value, _ in sources if kind == 'file']
296
+ short_bodies = [value for kind, value, flag in sources if kind == 'body' and flag == '-b']
297
+ if files or short_bodies:
298
+ if args[:2] not in (['gh', 'issue'], ['gh', 'pr']):
299
+ raise ValueError('ambiguous command')
300
+ if files:
301
+ if len(files) != 1 or len(sources) != 1 or not files[0] or files[0] == '-':
302
+ raise ValueError('ambiguous body file')
303
+ if not os.path.isabs(files[0]) and not base:
304
+ raise ValueError('unknown command checkout')
305
+ path = files[0] if os.path.isabs(files[0]) else os.path.join(base, files[0])
306
+ with open(path, encoding='utf-8') as body_file:
307
+ print(body_file.read(), end='')
308
+ else:
309
+ if len(sources) != 1 or not short_bodies[0]:
310
+ raise ValueError('ambiguous body')
311
+ print(short_bodies[0], end='')
312
+ raise SystemExit
313
+ except (OSError, UnicodeError, ValueError):
314
+ raise SystemExit(2)
240
315
  # P364: bash double-quote unescape. The double-quoted body capture groups
241
316
  # carry RAW shell-escaped command text — an orchestrator must backslash-escape
242
317
  # backticks (and \$, \", \\) inside \"...\" to survive bash parsing, e.g.
@@ -310,7 +385,9 @@ for pat, flags, unescape in patterns:
310
385
  body = unescape_dq(body)
311
386
  print(body)
312
387
  break
313
- " "$SURFACE" 2>/dev/null || echo "")
388
+ " "$SURFACE" "$TOOL_CWD" 2>/dev/null); then
389
+ BODY_FILE_ERROR=1
390
+ fi
314
391
  ;;
315
392
 
316
393
  Write|Edit)
@@ -362,6 +439,11 @@ print(json.dumps({
362
439
  " "$reason"
363
440
  }
364
441
 
442
+ if [ "$BODY_FILE_ERROR" -eq 1 ]; then
443
+ deny_with_reason "BLOCKED (external-comms gate): body-file could not be read unambiguously from the command checkout. Use one readable UTF-8 --body-file path and review its exact contents before retrying."
444
+ exit 0
445
+ fi
446
+
365
447
  permit_with_advisory() {
366
448
  local msg="$1"
367
449
  python3 -c "
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@windyroad/risk-scorer",
3
- "version": "0.19.13-preview.1284",
3
+ "version": "0.19.13",
4
4
  "description": "Pipeline risk scoring, commit/push gates, and secret leak detection",
5
5
  "scripts": {
6
6
  "prepack": "node scripts/sync-codex-skills.mjs --pack",