-
Notifications
You must be signed in to change notification settings - Fork 7
Expand file tree
/
Copy pathmain_logic.co
More file actions
80 lines (77 loc) · 4.55 KB
/
Copy pathmain_logic.co
File metadata and controls
80 lines (77 loc) · 4.55 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# NeMo Guardrails - Colang 2.x Entry Point
#
# BUG HISTORY (2026-08-05 / prompt_injection 50% gap fix):
# `flow main` previously matched a nonexistent event type, `UtteranceUser()`.
# NeMo Guardrails 2.x emits `UtteranceUserAction.Finished(final_transcript=...)`
# for user turns (see nemoguardrails/colang/v2_x/library/core.co). Because
# `UtteranceUser()` never matches any real event, `flow main` never advanced
# past its `match` statement for ANY input — benign or adversarial — which
# meant `CustomSelfCheckInputAction` (and therefore every Stage 1'/1B/1C/1D/1E
# /2/3 detector inside it) was NEVER invoked in production. The only reason
# any prompt_injection payloads were blocked at all was an unrelated Tier-1
# Aho-Corasick keyword pre-check (`_detect_bypass()` in
# src/gateway/governance/nemo/manager.py) that runs BEFORE generate_async()
# is ever called — a coincidental, narrow-coverage safety net, not the
# intended NeMo semantic pipeline.
#
# Confirmed via instrumented diagnostics: wrapping CustomSelfCheckInputAction
# and calling rails.generate_async(options={"rails": ["input"]}) against the
# unmodified config showed 0 invocations for both a canonical benign payload
# and an adversarial payload. After this fix, all 6 INJ-00x regression
# payloads correctly BLOCK and the benign canonical payload correctly ALLOWs.
#
# FIX: `flow main` now:
# 1. Imports `core` (the one NeMo 2.x standard-library file that parses
# cleanly under the installed lark grammar — `guardrails`, `llm`, and
# `timing` still fail to parse and remain unimported).
# 2. Matches the correct event: `UtteranceUserAction.Finished()`.
# 3. Explicitly and unconditionally awaits `CustomSelfCheckInputAction`
# with the user's text passed as the `content` argument — making the
# self-check a true "programmatic" rail that always runs on every turn,
# rather than relying on Colang's dialog-rail auto-routing (which
# requires `import guardrails`'s `_user_said` override and was never
# wired up, since that import still fails to parse in this NeMo build).
#
# We still do NOT `import guardrails` or `import llm` — the installed NeMo
# version's standard library files for those modules contain Colang 2.x
# syntax the lark parser rejects (UnexpectedToken at import statements).
# We also still do NOT use the `@override` decorator on `flow main` — that
# requires a pre-existing base flow of the same name to override, and no
# library `main` flow is imported here (see 2026-08-02 bug history below).
#
# BUG HISTORY (2026-08-02): The `@override` bug was previously masked by an
# unrelated Colang syntax error in generated_stpa_rails.co (lowercase `true`
# instead of `True`) that caused RailsConfig.from_path() to fail first,
# triggering the transparent-fallback path before `@override` was fixed. Once
# that syntax bug was fixed, LLMRails construction proceeded far enough to
# reach — and fail on — the `@override` defect. Both bugs independently
# disabled all NeMo semantic rails; both had to be fixed together.
#
# The Python validate_with_nemo() retains a "No main flow" pass-through guard
# for genuine Colang parse/construction failures; OPA + STPA remain
# authoritative defence-in-depth regardless of NeMo's semantic-rail status.
import core
flow main
match UtteranceUserAction.Finished() as $msg
$allowed = await CustomSelfCheckInputAction(content=$msg.final_transcript)
if not $allowed
await bot refuse to respond
# ---------------------------------------------------------------------------
# self check input — retained for config.yml's deprecated rails.input.flows
# ---------------------------------------------------------------------------
# config.yml still declares `rails.input.flows: [self check input]` (NeMo's
# deprecated YAML-based rail configuration). NeMo's LLMRails constructor
# validates at startup that every flow named in `rails.input.flows` actually
# exists (`_validate_config()` in nemoguardrails/rails/llm/llmrails.py) and
# hard-errors with InvalidRailsConfigurationError if it is missing — so this
# flow definition MUST remain even though `flow main` above no longer relies
# on Colang's dialog-rail auto-routing to reach it. It is dead code on the
# execution path (never invoked by `flow main`, and the `_user_said` override
# that would normally auto-invoke it via `run input rails` requires
# `import guardrails`, which is not imported here) but is required to satisfy
# NeMo's config-validation pass at construction time.
flow self check input
$allowed = await CustomSelfCheckInputAction
if not $allowed
bot refuse to respond
abort