Chapter 21: Governance and LLM Code Quality

May 19, 2026 · View on GitHub

NAAb includes a built-in governance engine that enforces project-level policies on polyglot code blocks. By placing a govern.json file in your project directory, you can control which languages are allowed, what APIs can be called, enforce code quality standards, and — uniquely — detect common LLM code generation failures like oversimplified stubs, incomplete error handling, and hallucinated APIs.

21.1 What is Governance?

Governance is policy-as-code for your polyglot blocks. Every polyglot block (<<python ... >>, <<javascript ... >>, etc.) is checked against your governance rules before execution. If a rule is violated, execution is blocked or a warning is emitted, depending on the enforcement level.

21.1.1 Three Enforcement Levels

NAAb uses a three-tier enforcement model inspired by HashiCorp Sentinel:

LevelBehaviorOverride
HARDBlock execution. Cannot be overridden.None
SOFTBlock execution. Can be overridden with --governance-override.--governance-override
ADVISORYWarn only. Execution continues.N/A (always continues)

21.1.2 Three Governance Modes

ModeBehavior
enforceNormal enforcement (default). Rules are checked and enforced.
auditDry-run. All rules are checked but nothing is blocked. Useful for testing new rules.
offGovernance disabled entirely.

21.1.3 Zero Overhead

When no govern.json file exists, the governance engine is completely inactive. There is no performance impact on programs that don't use governance.

21.2 Quick Start

Step 1: Create govern.json

Place a govern.json file in the same directory as your .naab file (or any parent directory):

{
  "version": "5.0",
  "mode": "enforce",

  "languages": {
    "allowed": ["python", "javascript"]
  },

  "code_quality": {
    "no_secrets": { "level": "hard" },
    "no_oversimplification": { "level": "soft" },
    "no_incomplete_logic": { "level": "soft" },
    "no_hallucinated_apis": { "level": "advisory" }
  }
}

Step 2: Run Your Program

naab-lang my_program.naab

If governance rules are loaded, you'll see:

[governance] Loaded: /path/to/govern.json (mode: enforce)

Step 3: Read the Summary

After execution, a summary is printed showing which checks passed, which were warned, and which were blocked:

=== Governance Summary ===
  PASS  code_quality.no_secrets [HARD]
  PASS  code_quality.no_oversimplification [SOFT]
  WARN  code_quality.no_hallucinated_apis [ADVISORY]
    ".push() is JavaScript — in Python, use .append()"

21.3 The govern.json Reference

The governance configuration file has 13 sections. All sections are optional — only configure what you need.

21.3.1 Top-Level Fields

{
  "version": "5.0",
  "mode": "enforce",
  "description": "My project governance rules"
}
FieldTypeDescription
versionstringConfig version. Use "3.0".
modestring"enforce", "audit", or "off".
descriptionstringHuman-readable description.

21.3.2 Languages

Control which languages can be used and set per-language rules:

{
  "languages": {
    "allowed": ["python", "javascript", "rust"],
    "blocked": ["shell"],
    "per_language": {
      "python": {
        "timeout": 15,
        "max_lines": 100,
        "banned_functions": ["eval(", "exec(", "compile("],
        "imports": {
          "mode": "blocklist",
          "blocked": ["subprocess", "ctypes", "os.system"]
        }
      },
      "javascript": {
        "timeout": 10,
        "banned_functions": ["eval(", "Function("],
        "imports": {
          "mode": "blocklist",
          "blocked": ["child_process", "fs"]
        }
      }
    }
  }
}

21.3.3 Capabilities

Control what polyglot blocks can access:

{
  "capabilities": {
    "network": { "enabled": false },
    "filesystem": { "mode": "read" },
    "shell": { "enabled": false }
  }
}

Capabilities also support the legacy flat format: "network": false.

21.3.4 Limits

Set resource limits for execution:

{
  "limits": {
    "timeout": {
      "global": 30,
      "per_block": 15
    },
    "execution": {
      "call_depth": 50,
      "polyglot_blocks": 20
    },
    "data": {
      "array_size": 10000,
      "string_length": 100000
    },
    "code": {
      "max_lines_per_block": 200,
      "max_nesting_depth": 8
    }
  }
}

21.3.5 Requirements

Enforce structural requirements:

{
  "requirements": {
    "main_block": {
      "level": "soft",
      "message": "Programs should have a main block"
    }
  }
}

21.3.6 Restrictions

Set security restrictions for dangerous operations:

{
  "restrictions": {
    "dangerous_calls": { "level": "hard" },
    "shell_injection": { "level": "hard" },
    "privilege_escalation": { "level": "hard" },
    "imports": {
      "level": "soft",
      "mode": "blocklist",
      "blocked": {
        "python": ["subprocess", "ctypes", "pickle"],
        "javascript": ["child_process", "fs", "net"]
      }
    }
  }
}

21.3.7 Code Quality

The largest section. Each check can be set as a simple string ("hard", "soft", "advisory") or as an object with detailed configuration:

{
  "code_quality": {
    "no_secrets": { "level": "hard" },
    "no_placeholders": "soft",
    "no_simulation_markers": { "level": "hard" },
    "no_oversimplification": { "level": "soft" },
    "no_incomplete_logic": { "level": "soft" },
    "no_hallucinated_apis": { "level": "advisory" }
  }
}

See sections 21.4 and 21.5 for the full list of code quality checks.

21.3.8 Custom Rules

Define your own regex-based governance rules:

{
  "custom_rules": [
    {
      "id": "CUSTOM-001",
      "name": "no_eval_python",
      "description": "Prevent eval() in Python",
      "pattern": "\\beval\\s*\\(",
      "languages": ["python"],
      "level": "hard",
      "message": "eval() is forbidden — use ast.literal_eval() or json.loads()",
      "enabled": true
    }
  ]
}

See section 21.8 for the full custom rules reference.

21.3.9 Output

Control how governance results are displayed:

{
  "output": {
    "summary": {
      "enabled": true,
      "format": "detailed",
      "show_passing": true
    },
    "errors": {
      "verbose": true,
      "show_help": true,
      "max_errors_per_rule": 3
    }
  }
}

21.3.10 Audit

Configure audit logging:

{
  "audit": {
    "level": "basic",
    "log_events": {
      "checks_passed": true,
      "checks_failed": true
    }
  }
}

21.3.11 Meta

Schema validation settings:

{
  "meta": {
    "schema_validation": {
      "warn_unknown_keys": true,
      "suggest_corrections": true
    }
  }
}

21.4 Code Quality Checks

NAAb provides 20 built-in code quality checks. Each can be enabled individually and set to any enforcement level.

Standard Checks

CheckDefault LevelDescription
no_secretshardDetects API keys, passwords, tokens (entropy-based + pattern matching)
no_placeholderssoftDetects TODO, FIXME, STUB, PLACEHOLDER markers
no_hardcoded_resultsadvisoryDetects fabricated return values like return {"status": "ok"}
no_piiadvisoryDetects SSNs, credit card numbers, email addresses
no_temporary_codesoftDetects temporary/throwaway code patterns
no_simulation_markershardDetects "Simulated", "Mock", "Fake" markers in code
no_mock_dataadvisoryDetects hardcoded mock/test data
no_apologetic_languageadvisoryDetects "sorry", "unfortunately" in code comments
no_dead_codeadvisoryDetects commented-out code blocks
no_debug_artifactssoftDetects console.log, print statements used for debugging
no_unsafe_deserializationhardDetects pickle.loads, yaml.load without SafeLoader
no_sql_injectionhardDetects string concatenation in SQL queries
no_path_traversalhardDetects ../../ path traversal patterns
no_hardcoded_urlsadvisoryDetects hardcoded URLs (configurable allowlist)
no_hardcoded_ipsadvisoryDetects hardcoded IP addresses
max_complexityadvisoryEnforces max lines, nesting depth, cyclomatic complexity
encodingadvisoryBlocks null bytes, Unicode BiDi attacks, homoglyphs

Example: Configuring no_secrets

"no_secrets": {
  "level": "hard",
  "entropy_check": {
    "enabled": true,
    "threshold": 4.5,
    "min_length": 20
  }
}

This enables Shannon entropy checking — strings with high randomness and sufficient length are flagged as potential secrets, even without matching known API key patterns.

21.5 LLM Anti-Drift Checks

These three checks specifically target common failures in LLM-generated code. They are designed to catch the patterns that make AI-generated code "look right" but behave incorrectly.

21.5.1 no_oversimplification

Purpose: Detect stub, trivial, and placeholder implementations where an LLM produced the minimal possible code instead of real logic.

What it catches:

  • Empty function bodies (def process(data): pass)
  • Trivial returns (def validate(x): return True)
  • Identity/passthrough functions (def transform(x): return x)
  • Not-implemented markers (raise NotImplementedError)
  • Comment-only bodies (# implementation here)
  • Fabricated results (return {"status": "ok"})

Example violation:

main {
    let result = <<python
def validate_user(user):
    return True  // <- Governance blocks this

def process_data(data):
    pass         // <- Governance blocks this
>>
}

Error output:

Error: Governance error: Oversimplified code: "def process_data(data):
    pass" [HARD-MANDATORY]

  Rule (govern.json): code_quality.no_oversimplification

  Help:
  - This looks like a stub or trivial implementation.
  - LLMs often produce minimal code that passes syntax checks but lacks real logic.

  Example:
    X Blocked:  def validate(data): return True
    V Allowed:  def validate(data):
                    if not isinstance(data, dict): raise TypeError(...)

Configuration:

"no_oversimplification": {
  "level": "soft",
  "check_empty_bodies": true,
  "check_trivial_returns": true,
  "check_identity_functions": true,
  "check_not_implemented": true,
  "check_comment_only_bodies": true,
  "check_fabricated_results": true,
  "case_sensitive": false,
  "min_function_lines": 2,
  "custom_patterns": []
}

Each sub-check can be toggled independently. For example, if you want to allow NotImplementedError in development but block empty bodies:

"no_oversimplification": {
  "level": "hard",
  "check_not_implemented": false,
  "check_empty_bodies": true
}

21.5.2 no_incomplete_logic

Purpose: Detect logic gaps, missing error handling, degenerate control flow, and shortcuts that indicate the LLM took a shortcut instead of implementing complete logic.

What it catches:

  • Empty/swallowed error handling (except: pass, empty catch blocks)
  • Generic/vague error messages (raise Exception("error"))
  • Bare raises (raise without exception type)
  • Degenerate loops (loops that return/break on first iteration)
  • Always-true/false conditions (if True:, if False:)
  • Placeholder error messages ("Something went wrong")

Example violation:

main {
    let result = <<python
try:
    data = open("file.txt").read()
except:
    pass  // <- Governance blocks this: swallowed exception
>>
}

Error output:

Error: Governance error: Incomplete logic: "except:
    pass" [HARD-MANDATORY]

  Rule (govern.json): code_quality.no_incomplete_logic

  Help:
  - This code has logic gaps that indicate shortcuts or lazy implementation.
  - Common issues: empty catch blocks, generic error messages, degenerate loops.

  Example:
    X Blocked:  except Exception: pass  # swallows all errors
    V Allowed:  except ValueError as e:
                    logger.error(f"Validation failed: {e}")
                    raise

Configuration:

"no_incomplete_logic": {
  "level": "soft",
  "check_empty_catch": true,
  "check_swallowed_exceptions": true,
  "check_generic_errors": true,
  "check_vague_error_messages": true,
  "check_single_iteration_loops": true,
  "check_bare_raise": true,
  "check_always_true_false": true,
  "check_missing_validation": true,
  "case_sensitive": false,
  "custom_patterns": []
}

21.5.3 no_hallucinated_apis

Purpose: Detect calls to non-existent APIs, wrong method names, and cross-language confusion. LLMs frequently mix up APIs between Python and JavaScript — this check catches those mistakes and suggests the correct alternative.

What it catches:

Python blocks using JavaScript APIs:

  • .push() instead of .append()
  • .length instead of len()
  • console.log() instead of print()
  • JSON.stringify() instead of json.dumps()
  • Math.floor() instead of math.floor()
  • === instead of ==
  • null instead of None

JavaScript blocks using Python APIs:

  • print() instead of console.log()
  • len() instead of .length
  • .append() instead of .push()
  • .strip() instead of .trim()
  • True/False/None instead of true/false/null
  • and/or/not instead of &&/||/!

Cross-language syntax confusion:

  • // comments in Python (should be #)
  • # comments in JavaScript (should be //)

Example violation:

main {
    let result = <<python
data = [1, 2, 3]
data.push(4)       // <- ".push() is JavaScript — in Python, use .append()"
length = data.length  // <- ".length is JavaScript — in Python, use len()"
>>
}

Error output:

Error: Governance error: Hallucinated API in python block: ".length" [SOFT-MANDATORY]

  Rule (govern.json): code_quality.no_hallucinated_apis

  Help:
  - .length is JavaScript — in Python, use len()

Configuration:

"no_hallucinated_apis": {
  "level": "advisory",
  "check_cross_language": true,
  "check_made_up_functions": true,
  "check_wrong_syntax": true,
  "case_sensitive": true,
  "custom_patterns": []
}

The check is language-aware: it automatically selects the appropriate pattern set based on the polyglot block's language header (<<python, <<javascript, etc.).

21.6 Security Checks

Beyond code quality, governance includes dedicated security checks:

CheckDefault LevelDescription
shell_injectionhardDetects shell injection patterns (backticks, $(...), os.system)
code_injectionhardDetects dynamic code execution (eval, exec, Function())
privilege_escalationhardDetects sudo, chmod 777, os.setuid
data_exfiltrationhardDetects base64 encoding + network send patterns
resource_abusesoftDetects fork bombs, crypto mining patterns
info_disclosureadvisoryDetects information leakage patterns
crypto_weaknessadvisoryDetects weak crypto (MD5, SHA1, DES, ECB mode)

These are configured in the restrictions section of govern.json.

21.7 Per-Language Rules

Each language can have its own set of rules for fine-grained control:

{
  "languages": {
    "per_language": {
      "python": {
        "timeout": 15,
        "max_lines": 100,
        "banned_functions": ["eval(", "exec("],
        "imports": {
          "mode": "blocklist",
          "blocked": ["subprocess", "ctypes"]
        },
        "style": {
          "no_star_imports": { "level": "advisory" }
        }
      },
      "javascript": {
        "timeout": 10,
        "max_lines": 80,
        "banned_functions": ["eval(", "Function("],
        "style": {
          "strict_mode": { "level": "soft" },
          "no_var": { "level": "advisory" },
          "no_console_log": { "level": "advisory" }
        }
      },
      "shell": {
        "timeout": 5,
        "banned_commands": ["rm -rf", "mkfs"],
        "style": {
          "require_set_e": { "level": "soft" },
          "no_curl_pipe_sh": { "level": "hard" }
        }
      }
    }
  }
}

21.8 Custom Rules

For project-specific policies, define custom regex-based rules:

{
  "custom_rules": [
    {
      "id": "PROJ-001",
      "name": "no_hardcoded_urls",
      "description": "All URLs must come from config",
      "pattern": "https?://(?!localhost|127\\.0\\.0\\.1)",
      "languages": ["python", "javascript"],
      "level": "soft",
      "message": "Hardcoded URLs are not allowed. Use config.get_url() instead.",
      "help": "Move URLs to your configuration file",
      "good_example": "url = config.get_url('api')",
      "bad_example": "url = 'https://api.example.com'",
      "enabled": true,
      "case_sensitive": false
    },
    {
      "id": "PROJ-002",
      "name": "no_print_debug",
      "description": "No debug print statements",
      "pattern": "print\\(.*[Dd]ebug",
      "languages": [],
      "level": "advisory",
      "message": "Debug print statements should be removed",
      "enabled": true
    }
  ]
}

Custom Rule Fields

FieldRequiredDescription
idYesUnique rule identifier (shown in errors)
nameYesShort name for the rule
descriptionNoHuman-readable description
patternYesRegex pattern to match violations
languagesNoLanguages to apply to (empty = all languages)
levelNoEnforcement level (default: "hard")
messageNoError message shown on violation
helpNoHelp text explaining how to fix
good_exampleNoExample of correct code
bad_exampleNoExample of incorrect code
enabledNoWhether rule is active (default: true)
case_sensitiveNoCase-sensitive matching (default: false)

21.9 CLI Flags and Reports

Governance CLI Flags

FlagDescription
--governance-overrideOverride SOFT-level governance rules (HARD rules cannot be overridden)
--governance-report <path>Write JSON governance report to file
--governance-sarif <path>Write SARIF report (for IDE/CI integration)
--governance-junit <path>Write JUnit XML report (for CI pipelines)

Examples

# Run with governance and override soft rules
naab-lang --governance-override my_program.naab

# Generate a JSON report for review
naab-lang --governance-report report.json my_program.naab

# Generate SARIF for GitHub Code Scanning
naab-lang --governance-sarif results.sarif my_program.naab

# Generate JUnit for CI (Jenkins, GitLab CI, etc.)
naab-lang --governance-junit results.xml my_program.naab

SARIF Integration

SARIF (Static Analysis Results Interchange Format) reports can be uploaded to GitHub Code Scanning:

# .github/workflows/governance.yml
- name: Run NAAb Governance
  run: naab-lang --governance-sarif results.sarif my_program.naab

- name: Upload SARIF
  uses: github/codeql-action/upload-sarif@v3
  with:
    sarif_file: results.sarif

21.10 Best Practices

For CI/CD Pipelines

Use enforce mode with --governance-sarif or --governance-junit for machine-readable results:

{
  "version": "5.0",
  "mode": "enforce",
  "code_quality": {
    "no_secrets": { "level": "hard" },
    "no_sql_injection": { "level": "hard" },
    "no_path_traversal": { "level": "hard" },
    "no_unsafe_deserialization": { "level": "hard" }
  }
}

For LLM-Generated Code Review

Enable all three LLM anti-drift checks to catch the most common AI code failures:

{
  "version": "5.0",
  "mode": "enforce",
  "code_quality": {
    "no_oversimplification": { "level": "hard" },
    "no_incomplete_logic": { "level": "hard" },
    "no_hallucinated_apis": { "level": "soft" },
    "no_simulation_markers": { "level": "hard" },
    "no_placeholders": { "level": "soft" }
  }
}

For Team Projects

Start with audit mode to understand what would be flagged, then switch to enforce:

{
  "version": "5.0",
  "mode": "audit",
  "description": "Team governance rules (audit mode for rollout)",
  "languages": {
    "allowed": ["python", "javascript"]
  },
  "code_quality": {
    "no_secrets": { "level": "hard" },
    "no_oversimplification": { "level": "soft" },
    "no_incomplete_logic": { "level": "advisory" }
  }
}

Gradual Adoption

  1. Start with "mode": "audit" to see what gets flagged without blocking anything
  2. Enable security checks first (no_secrets, no_sql_injection, no_unsafe_deserialization)
  3. Add LLM checks at "advisory" level, then promote to "soft" or "hard" as confidence grows
  4. Use custom_rules for project-specific patterns
  5. Generate reports with --governance-sarif for CI integration

21.10 Project Context Awareness

NAAb governance can read your existing project files and use them to supplement govern.json. This turns governance from a static config into an adaptive partner that understands your project holistically.

Key principle: govern.json always wins. Project context supplements, never overrides.

Enabling

{
  "project_context": {
    "enabled": true
  }
}

NAAb scans upward from the script directory for known files and extracts rules from three layers:

LayerFilesWhat Gets Extracted
LLM filesCLAUDE.md, .cursorrules, copilot-instructions.md, gemini.mdLanguage preferences, banned functions, style rules
Linter configs.editorconfig, .eslintrc.json, .prettierrc, biome.jsonIndent style/size, banned patterns
Package manifestspackage.json, go.mod, Cargo.toml, pyproject.tomlLanguage detection (advisory only)

Each layer is independently toggleable via sources.llm, sources.linters, sources.manifests.

NAAb extracts clear directives from LLM files using strong verbs ("always use", "never use", "prefer X for Y"). Ambiguous text and content inside code blocks is skipped. Extracted language preferences feed into polyglot optimization scoring.

Conflicts are resolved by priority_source (if set), otherwise linter configs win over LLM files, which win over manifests. Every skipped directive is reported with source and reason. Use suppress_rules to surgically suppress specific extractions by ID, or dry_run: true to preview without applying.

See docs/govern-template.json for the full configuration reference.

21.11 Taint Tracking

Taint tracking is NAAb's most powerful security feature, introduced in governance v4.0. It automatically tracks untrusted data from where it enters your program (sources) through all transformations to where it leaves (sinks), blocking unsafe usage unless the data has been sanitized.

21.11.1 How It Works

  1. Sources mark data as tainted when it enters from untrusted origins
  2. Propagation carries taint through string concatenation, interpolation, function returns, loop iterators, and container mutations
  3. Sanitizers clear taint when data passes through validation functions
  4. Sinks check for tainted data before performing dangerous operations

21.11.2 Configuration

{
  "taint_tracking": {
    "enabled": true,
    "level": "hard",
    "sources": ["env.get", "io.read_line", "file.read", "polyglot_output"],
    "sinks": ["shell_exec", "python_exec", "go_exec", "javascript_exec",
              "nim_exec", "file.write", "file.append", "http.", "env.set_var"],
    "sanitizers": ["validate_", "sanitize_", "escape_"],
    "propagation": {
      "string_concat": true,
      "string_interpolation": true,
      "function_returns": true,
      "for_loop_iterators": true,
      "dict_array_access": true
    }
  }
}

21.11.3 Sources

Sources are functions whose return values are automatically marked as tainted:

SourceWhat It Tracks
env.getEnvironment variables (attacker-controlled in CI/CD)
io.read_lineUser input from stdin
file.readFile contents (may be attacker-supplied)
polyglot_outputReturn values from polyglot blocks
polyglot_output:pythonOnly Python block output (per-language)

Per-language polyglot sources: Use "polyglot_output:python" to taint only Python block output while leaving Go/JS/etc. untainted. This is useful when one language is trusted but another is not.

21.11.4 Sinks

Sinks are operations that must not receive tainted data:

SinkWhat It Protects Against
shell_execCommand injection in shell blocks
python_execCode injection in Python blocks
go_exec, nim_exec, etc.Code injection in compiled language blocks
file.write, file.appendWriting untrusted data to files
http.SSRF (Server-Side Request Forgery) via tainted URLs
env.set_varEnvironment variable injection

Prefix matching: "http." matches http.get, http.post, http.put, http.delete — any HTTP method.

21.11.5 Sanitizers

Sanitizer functions clear taint from variables. Matching uses prefix matching:

Sanitizer PrefixMatches
validate_validate_input(), validate_url(), etc.
sanitize_sanitize_html(), sanitize_sql(), etc.
escape_escape_shell(), escape_html(), etc.
int(int(user_input) — type casting as sanitization
float(float(user_input) — type casting as sanitization

Member expressions work: module.sanitize_foo(x) also clears taint.

Important: Prefix matching means "validate_" matches validate_input but NOT revalidate_input.

21.11.6 Propagation

Taint propagates automatically through these paths:

// String concatenation
let tainted = env.get("USER_INPUT")
let msg = "Hello " + tainted           // msg is tainted

// String interpolation
let greeting = "Hello ${tainted}"       // greeting is tainted

// Function returns
fn get_data() {
    let x = env.get("DATA")
    return x                            // return value carries taint
}
let data = get_data()                   // data is tainted

// For-loop iterators
for item in env.get("PATH") {           // item is tainted
    print(item)                         // safe (print is not a sink)
}

// Container mutations
let arr = [1, 2, 3]
arr.push(tainted)                       // arr is now tainted
let dict = {"key": "clean"}
dict["key"] = tainted                   // dict is now tainted

21.11.7 Examples

Example 1: Command injection prevention

use env

fn process_user_input() {
    let user_file = env.get("USER_FILE")  // tainted

    // BLOCKED: tainted data in shell block
    // let result = <<shell[user_file]
    //   cat ${user_file}
    // >>

    // SAFE: sanitize first
    let safe_file = validate_filename(user_file)  // taint cleared
    let result = <<shell[safe_file]
      cat ${safe_file}
    >>
    return result
}

fn validate_filename(name) {
    // Real validation logic here
    if string.contains(name, "..") {
        return "invalid"
    }
    return name
}

main {
    process_user_input()
}

Example 2: SSRF prevention

use env
use http

main {
    let url = env.get("WEBHOOK_URL")  // tainted

    // BLOCKED: tainted URL passed to http.get
    // let response = http.get(url)

    // SAFE: validate the URL first
    let safe_url = validate_url(url)
    let response = http.get(safe_url)
}

fn validate_url(url) {
    if !string.starts_with(url, "https://") {
        return "https://default.example.com"
    }
    return url
}

Example 3: Async taint propagation

use env

async fn fetch_data() {
    let key = env.get("API_KEY")  // tainted
    return key
}

main {
    let data = await fetch_data()  // data is tainted (async propagation)
    // Taint flows through await expressions
}

21.11.8 Enforcement Levels

LevelBehavior
hardTaint violation throws an error. Execution stops. Cannot be overridden.
softTaint violation throws an error. Can be overridden with --governance-override.
advisoryTaint violation emits a warning. Execution continues.

21.11.9 Limitations

  • Async taint is snapshot-based: Each async function gets a snapshot of the parent's taint state at creation time. Multi-level async chains (grandchild functions) may not see taint added by intermediate functions.
  • Module imports skip body scan: Taint is enforced at runtime, not during static import analysis. This is a performance optimization.
  • Name-based tracking: Taint is tracked by variable name, not by value identity. Reassigning a variable with clean data clears its taint.

21.12 Cross-Module Contracts

Governance v4 adds parameter type validation at function call sites:

{
  "contracts": {
    "validate_inputs": true,
    "functions": {
      "process_data": {
        "params": ["data:dict", "count:int"],
        "return_type": "dict",
        "return_keys": ["status", "result"]
      },
      "calculate_score": {
        "params": ["values:array"],
        "return_type": "number",
        "return_min": 0,
        "return_max": 100
      }
    }
  }
}

Contracts are checked on both callFunction() and direct call dispatch paths, with violations logged to the audit trail.

21.13 Enhanced Audit Trail

Governance v4 provides JSONL audit logging with tamper-evident hash chains:

{
  "audit": {
    "level": "detailed",
    "log_file": "governance_audit.jsonl",
    "log_events": {
      "polyglot_timing": true,
      "taint_decisions": true,
      "contract_checks": true,
      "checks_passed": true,
      "checks_failed": true
    }
  }
}

Each audit entry includes a SHA-256 hash of the previous entry, creating a tamper-evident chain. Events logged include:

  • polyglot_timing: Execution duration per polyglot block
  • taint_decisions: Every taint mark/clear/block decision
  • contract_checks: Every contract pass/fail with parameter details

21.14 The NAAb Scanner

NAAb includes a built-in static analysis scanner accessible via naab-lang --scan:

# Scan a Python file
naab-lang --scan script.py python

# Auto-detect language
naab-lang --scan src/utils.js auto

# Scan with JSON output
naab-lang --scan script.py python --governance-report report.json

The scanner provides 139 checks across 6 categories:

CategoryChecksWhat It Detects
Redundancy30Dead variables, unused imports, duplicate expressions
Code Quality20Magic numbers, long functions, deep nesting
Complexity9Cyclomatic complexity, cognitive complexity
Style12Naming conventions, formatting issues
Security0(Covered by governance security checks)
Language-specific68Python (14), JavaScript (12), C++ (12), Go (9), Rust (10), NAAb (11)

The scanner is configurable via the "scanner" section of govern.json and supports text, JSON, and SARIF output formats.

21.15 Multi-Agent Governance

NAAb governance v4.0 introduces multi-agent role enforcement, allowing govern.json to define per-agent restrictions on languages and file paths. This is designed for scenarios where multiple AI agents or automated tools interact with the same codebase.

21.15.1 Agent Roles Configuration

Add an agent_roles section to your govern.json:

{
  "agent_roles": {
    "code-bot": {
      "allowed_languages": ["python", "javascript"],
      "allowed_paths": ["./src", "./lib"],
      "blocked_paths": ["./secrets", "./.env", "./credentials"]
    },
    "test-bot": {
      "allowed_languages": ["python", "shell"],
      "allowed_paths": ["./tests", "./fixtures"],
      "blocked_paths": ["./src"]
    },
    "deploy-bot": {
      "allowed_languages": ["shell"],
      "allowed_paths": ["./deploy", "./scripts"],
      "blocked_paths": ["./src", "./tests"]
    }
  }
}

Each agent role defines:

FieldTypeDescription
allowed_languagesarrayLanguages this agent may use in polyglot blocks
allowed_pathsarrayFilesystem paths this agent may read/write
blocked_pathsarrayFilesystem paths this agent is explicitly denied access to

21.15.2 CLI Usage

Use the --agent-id flag to identify the executing agent:

# Run as a specific agent
naab-lang --agent-id code-bot my_program.naab

# Combine with governance dashboard for summary output
naab-lang --agent-id code-bot --governance-dashboard my_program.naab

When --agent-id is provided, the governance engine:

  1. Looks up the agent name in govern.jsonagent_roles
  2. Restricts the agent's polyglot blocks to only allowed_languages
  3. Restricts file operations to allowed_paths and blocks blocked_paths
  4. If the agent is not listed in agent_roles, default governance rules apply (no agent-specific restrictions)

21.15.3 Governance Dashboard

The --governance-dashboard flag outputs a governance summary to stderr after execution:

naab-lang --agent-id code-bot --governance-dashboard my_program.naab

Output:

=== Governance Dashboard ===
  Agent: code-bot
  Checks: 12 passed, 0 warned, 0 blocked
  Languages used: python (2 blocks), javascript (1 block)
  Paths accessed: ./src/utils.naab, ./lib/helpers.naab
  Taint: 3 sources, 2 sanitized, 0 violations

21.15.4 Telemetry

Enable JSONL telemetry output to track agent execution over time:

{
  "telemetry": {
    "enabled": true,
    "output_file": "telemetry.jsonl"
  }
}

Each execution appends a JSONL record with:

  • Agent ID (if provided via --agent-id)
  • Timestamp
  • Languages used
  • Paths accessed
  • Governance check results (passed/warned/blocked)
  • Execution duration

This data can be used for trend analysis, compliance reporting, and monitoring agent behavior across runs.

21.15.5 Best Practices for Multi-Agent Governance

  1. Principle of least privilege: Give each agent only the languages and paths it needs
  2. Block sensitive paths: Always block ./secrets, ./.env, and credential directories for all agents
  3. Use telemetry for auditing: Enable JSONL telemetry to maintain a log of agent activities
  4. Combine with taint tracking: Agent roles + taint tracking provides defense-in-depth — even if an agent accesses allowed paths, tainted data is still blocked at sinks
  5. Test with --governance-dashboard: Use the dashboard flag during development to verify agent restrictions are correctly applied

21.16 Behavioral Sequence Detection (BSD)

Beyond static checks, NAAb can detect dangerous multi-step behavior patterns at runtime. BSD uses finite state machine matching over an event ring buffer to catch attack sequences like "read secrets, encode them, then exfiltrate."

21.16.1 Configuration

Patterns are fully user-defined in govern.json — there are no hardcoded rules:

{
  "behavioral_sequences": {
    "enabled": true,
    "window_size": 100,
    "patterns": [
      {
        "name": "data_exfiltration",
        "sequence": ["env.get:*KEY*|file.read:*secret*", "encode|base64|compress", "http.post|agent.send"],
        "level": "hard",
        "max_gap": 10,
        "decay_seconds": 300,
        "decay_turns": 20,
        "rationale": "Reading sensitive data, encoding it, then sending it out matches exfiltration."
      }
    ]
  }
}

Each step in the sequence is a pipe-separated list of matchers. The optional colon suffix is a detail glob that matches against function arguments (e.g., env.get:*KEY* matches env.get("API_KEY") but not env.get("HOME")).

21.16.2 Key Features

  • Pre-execution blocking — when BSD detects that the next action would complete a dangerous sequence, it blocks that action before it executes
  • Decay/expiry — events older than decay_turns turns or decay_seconds seconds no longer count toward sequence matches, preventing false positives from stale context
  • Detail globs — per-argument filtering with pipe-separated alternatives (env.get:*SECRET*|*TOKEN*)
  • Cumulative scoring — BSD matches feed into the cumulative risk score like any advisory check

21.16.3 Enforcement Levels

BSD patterns support the same three enforcement levels as other governance checks:

LevelBehavior
hardBlock execution immediately. No override.
softBlock execution unless --governance-override is passed.
advisoryWarn and add to cumulative risk score.

21.17 Context Drift Detection (CDD)

CDD monitors LLM agent conversations for coherence drift — detecting when an agent's behavior diverges from its declared intent.

21.17.1 Configuration

{
  "context_drift": {
    "enabled": true,
    "level": "advisory",
    "coherence_threshold": 0.5,
    "max_contradictions": 3,
    "check_interval_turns": 5,
    "fingerprint_window": 10,
    "signals": {
      "repeated_failures": true,
      "circular_actions": true,
      "scope_creep": true
    },
    "weights": {
      "circular": 0.3,
      "scope_creep": 0.2,
      "contradiction": 0.4,
      "repeated_failure": 0.1
    }
  }
}

21.17.2 Drift Signals

SignalWhat It Detects
repeated_failuresAgent retrying the same failing action multiple times
circular_actionsAgent cycling through the same set of actions without progress
scope_creepAgent expanding beyond its declared intent
contradictionAgent taking actions that contradict previous decisions

Each signal has a configurable weight. The coherence score starts at 1.0 and decays as drift signals accumulate. When it drops below coherence_threshold, CDD triggers enforcement.

21.17.3 Integration with Agent Module

CDD is automatically checked during agent.send() and agent.batch() calls. Governance notices are surfaced in the return dict via the governance_notices field, allowing the calling code to react to drift warnings.

21.18 Cumulative Risk Scoring

Advisory findings don't block execution, but they accumulate. NAAb's cumulative scoring system tracks the total weighted risk across all advisory checks in a single execution.

21.18.1 How It Works

Each advisory check that fires adds its weight to a cumulative score. The score is compared against configurable thresholds:

ZoneDefault RangeBehavior
Green0 — below yellow thresholdNo action
Yellowyellow threshold — below red thresholdWarning emitted
RedAt or above red thresholdExecution blocked (quality gate)

21.18.2 Configuration

Global scoring configuration:

{
  "scoring": {
    "enabled": true,
    "default_weight": 1.0,
    "green_threshold": 0,
    "yellow_threshold": 20,
    "red_threshold": 40,
    "threshold_mode": "cumulative"
  }
}

Named scorer configurations can override the global defaults for specific contexts (e.g., agent review):

{
  "scorers": {
    "security": {
      "enabled": true,
      "default_weight": 1.0,
      "rule_weights": {},
      "green_threshold": 0,
      "yellow_threshold": 20,
      "red_threshold": 40,
      "threshold_mode": "cumulative"
    }
  }
}

Named scorers are referenced by agent_review.scorer to use a specific scoring profile during AI-assisted code review.