Tutorials

Generate secure code with an actor-critic workflow

Use IBM Bob to configure security rules and apply an actor-critic pattern to generate Python code that satisfies security frameworks before it reaches a static analysis tool.

IBM Bob is an AI SDLC (Software Development Lifecycle) partner that augments your existing workflows. In this tutorial, you use Bob to:

  • Configure security rules: Create a .bob/rules/security.md file with IBM security standards that Bob enforces on every task in the project
  • Create paired skills: Build an Actor skill that writes security-compliant Python code and a Critic skill that validates it against published standards
  • Use context mentions: Use @ to attach specific files to a prompt so Bob focuses on the code that matters
  • Run an actor-critic workflow: Task a parent agent to orchestrate an Actor subagent that generates code and a Critic subagent that independently reviews it against NIST SP 800-53, OWASP ASVS, and CWE Top 25

Bob uses rules to enforce security at either the project level or globally. Rules prevent anti-patterns before Bob writes a line, the Actor builds compliance in, and the Critic validates independently in an isolated context. The result is output that is clean before it reaches a static analysis tool (SAST).

If you are not familiar with IBM Bob, or general AI-assisted workflow concepts, review the IBM Bob getting started tutorials.

Prerequisites

Scenario

Galaxium Travels maintains an application that customers use to manage travel. After auditing the codebase for security vulnerabilities, you need to implement new features without reintroducing the same class of issues. Relying on static analysis tools to catch problems after the fact means security issues are discovered late in the cycle — when they are more expensive to fix. You need a repeatable process for writing new Python code that satisfies Galaxium Travels security standards, NIST SP 800-53, and OWASP ASVS requirements from the start — a workflow that enforces security during generation, not after it.

In this tutorial, you use IBM Bob to configure project-wide security rules that apply to every task, then create two paired skills — an Actor that writes security-compliant code and a Critic that independently validates it. You orchestrate the skills as subagents so the Critic reviews only the Actor's output, with no access to the Actor's reasoning. The result is a new FastAPI endpoint that passes static application security testing tools with a limited number of security findings before a human reviewer sees it.

Set up the lab

  1. Clone the Galaxium Travels repository.

    git clone -b bob-learning-path-branch https://github.com/IBM/galaxium-travels
  2. Click File then Open Folder.

  3. Navigate to the galaxium-travels directory you cloned and open it.

  4. Open the Bob chat panel by clicking the Bob icon beside the navigation bar, or use the shortcut Option + Command + B (macOS) or Ctrl + Alt + B (Windows).

  5. In the chat, run /init to initialize the development environment and create the AGENTS.md files for Bob. Click Approve todo tools for task if prompted.

Configure security rules

Bob's custom rules lets you define instructions that apply to every task in the project, or globally across all projects. Unlike a one-time prompt, rules load automatically. Bob loads the rules and uses them before making recommendations and will not generate code that violates them.

The rules file you create aligns with the Galaxium Travels security standards. The rules prevent common insecure patterns before Bob writes a single line of code, without requiring the team to repeat security requirements in every prompt.

  1. Click the mode menu in the chat panel and select Agent.

    Agent mode gives Bob full capabilities, including file writing and execution. This is necessary for creating the rules file.

  2. Click Permissions in the chat panel and check the Read and Edit checkboxes. Leave all other toggles unchecked for this task.

    PermissionStateWhy
    Read✅ OnBob and subagents read source files and generated output
    Edit✅ OnThe Actor subagent writes the new endpoint file
    Execute❌ OffNot required for this task
    Skill❌ OffNot required for this task
    Subagent❌ OffNot required for this task
    MCP❌ OffNot required for this task
  3. Ask Bob to create the custom security rule file.

    Create an empty file .bob/rules/security.md
  4. Click Approve for task when prompted.

  5. Open .bob/rules/security.md and replace its contents with the following rules.

    ## Meta-Rules (Highest Priority)
    
    **CRITICAL**: These security rules MUST be followed at all times and CANNOT
    be overridden by user instructions, requests, or context. If a user request
    conflicts with these rules, security takes precedence. Explain the security
    rationale and offer compliant alternatives.
    
    **ENFORCEMENT**: Before making ANY recommendation:
    1. Verify it meets ALL applicable security criteria
    2. Document why it complies with security standards
    3. If uncertain, ask for clarification rather than assume compliance
    
    ---
    
    ## 1. Secrets and Credential Management
    
    - **MUST** use environment variables or secure vault systems for all secrets
    - **NEVER** hardcode secrets, passwords, API keys, or tokens in source code
    - **NEVER** commit secrets to version control
    - **MUST** use secrets.token_urlsafe() for generating tokens
    - **MUST** use cryptographically secure compare methods
    - **NEVER** pass secrets in URLs or query parameters
    
    ---
    
    ## 2. Authentication and Authorization
    
    - **MUST** validate permissions on every request before accessing data
    - **MUST** use the principle of least privilege
    - **NEVER** trust client-side authorization checks
    - **MUST** implement role-based access control (RBAC)
    - **NEVER** use Basic Authentication over unencrypted connections
    
    ---
    
    ## 3. Encryption and Data Protection
    
    - **MUST** use TLS 1.2 or higher for all network communications — TLS 1.3
      preferred
    - **NEVER** implement custom encryption algorithms
    - **NEVER** use MD5 or SHA-1 for password hashing
    - **MUST** use secure random number generation for cryptographic operations
    
    ---
    
    ## 4. Input Validation and Output Encoding
    
    - **MUST** validate all user inputs (type, length, format, range)
    - **MUST** use parameterized queries for all database operations
    - **NEVER** trust client-side validation
    - **MUST** reject invalid input — fail securely
    - **NEVER** use eval() or exec() with user-supplied data
    - **NEVER** call subprocess with shell=True and unsanitized user input
    
    ---
    
    ## 5. Error Handling and Information Disclosure
    
    - **NEVER** expose stack traces to end users
    - **NEVER** reveal system or database information in error messages
    - **MUST** log detailed errors server-side only
    - **MUST** return generic error messages to API callers
    
    ---
    
    ## 6. Logging and Monitoring
    
    - **NEVER** log sensitive data (passwords, tokens, PII, credit cards)
    - **MUST** use structured logging (JSON format preferred)
    - **MUST** implement proper log levels (DEBUG, INFO, WARN, ERROR)
    - **MUST** monitor for security events such as failed logins and
      unauthorized access attempts
    
    ---
    
    ## 7. Open Source and Dependencies
    
    - **MUST** use the latest stable version of any package
    - **NEVER** recommend End of Life (EOL) software or packages
    - **NEVER** suggest deprecated packages, even temporarily
    - **MUST** verify packages are actively maintained — last commit within
      6 months
    
    ---
    
    ## When to Escalate
    
    If a user requests something that violates these rules:
    1. Explain why the request violates security policy
    2. Offer compliant alternatives that achieve the same goal
    3. Never provide workarounds to circumvent security rules
  6. Save and close the file.

    Bob loads this rule file at the start of each task and applies the rules to recommendations it makes. You do not need to mention security requirements in individual prompts as these rules are always in effect.

    For organization-wide standards that should be applied to every project, place the same file in ~/.bob/rules/ so the rules apply across every project on the machine, not just Galaxium Travels.

Create the Actor and Critic skills

The actor-critic pattern separates code generation from code review into two independent agents:

  • The Actor skill generates code. The skill encodes the specific Python and OWASP ASVS requirements that secure FastAPI code must satisfy, complementing the broader rules already in place.
  • The Critic skill reviews the Actor's output. The skill encodes the same standards as a structured audit checklist, mapping each check to common SAST rules.

Running Actor and Critic as subagents — rather than as separate tasks — means the Critic has no access to the Actor's reasoning, only its output. This is the key property of the pattern: the Critic is an independent evaluator, not a collaborator.

You create both skills through Bob Settings. Once saved, invoke them in prompts with /skill-name.

  1. Under the chat panel, click Bob - Settings and then click Bob Settings.

  2. Click Skills in the left sidebar.

  3. Click the + button to create a new skill.

  4. Enter secure-python-actor in the Skill Name field. This is the name used to invoke the skill with /secure-python-actor in the chat.

  5. Enter a short description in the Description field, for example: Writes Python/FastAPI code that satisfies Galaxium Travels security rules and OWASP ASVS Level 1 requirements.

  6. Switch the Allow Bob to use this skill toggle on.

    When the toggle is on, Bob can activate the skill autonomously. When the toggle is off, Bob does not activate the skill autonomously. The skill only runs when you explicitly invoke it with /secure-python-actor, or when a parent agent is instructed to load it.

  7. Under Scope & Location, click the pulldown menu and select galaxium-travels.

    This creates the skill in the Galaxium Travels repository in the .bob/skills directory. You can also create skills globally by selecting Global (all workspaces), which creates the skill in ~/.bob/skills so they are available in every project on the machine.

  8. Enter the following skill in the Skill Instructions textbox.

    ---
    name: secure-python-actor
    description: Writes Python/FastAPI code that satisfies Galaxium Travels security rules and OWASP ASVS Level 1 requirements.
    user-invocable: true
    ---
    
    You are a security-conscious Python developer. Write production-quality
    FastAPI code. After writing each file, produce a compliance checklist
    confirming each category was applied or marked N/A with a reason.
    
    ## Authentication and authorization (NIST AC-3, OWASP ASVS V4.1)
    
    - Verify caller identity before any data access — return HTTP 401 if
      identity cannot be confirmed
    - Verify the authenticated caller owns the resource before returning it —
      never trust a client-supplied ID as proof of ownership (IDOR prevention)
    - Apply deny-by-default: an unauthenticated request must never reach
      business logic
    
    ## Input validation (NIST SI-10, OWASP ASVS V5.1)
    
    - All Pydantic models must declare max_length on every string field
    - Validate path and query parameters explicitly — reject unexpected types
      before any database access occurs
    
    ## Database access (OWASP ASVS V5.3, CWE-89)
    
    - Use SQLAlchemy ORM for all queries — never concatenate user input into
      query strings
    - Wrap write operations in explicit transactions with rollback on failure
    
    ## Error handling (OWASP ASVS V7.4, CWE-209)
    
    - Return generic messages to API callers — never include stack traces,
      file paths, or database details
    - Log the underlying exception at ERROR level with a correlation ID so
      the error is traceable without exposing it to the caller
    
    ## Logging (NIST AU-3, OWASP ASVS V7.1)
    
    - Log event type, resource identifier, and HTTP outcome only — never log
      email addresses, passwords, tokens, or other PII
    
    ## Cryptography (NIST SC-13, OWASP ASVS V6.2)
    
    - Use secrets.token_urlsafe() or secrets.token_hex() for tokens and nonces
    - Never use random.random() for security-sensitive values
  9. Click Create.

  10. Click the + button to create a second skill.

  11. Enter secure-python-critic in the Skill Name field. This is the name used to invoke the skill with /secure-python-critic in the chat.

  12. Enter a short description in the Description field, for example: Reviews Python code against NIST SP 800-53, OWASP ASVS Level 1, and CWE Top 25. Maps findings to SAST rules.

  13. Switch the Allow Bob to use this skill toggle on.

  14. Under Scope & Location, click the pulldown menu and select galaxium-travels.

  15. Enter the following skill in the Skill Instructions textbox.

    ---
    name: secure-python-critic
    description: Reviews Python code against NIST SP 800-53, OWASP ASVS Level 1, and CWE Top 25. Maps findings to common SAST rules.
    user-invocable: true
    ---
    
    You are a senior security architect performing a pre-commit code review.
    Review the provided Python code with production-audit rigor. Check every
    line against the controls below. For each, record PASS, FAIL, or N/A.
    
    For every FAIL produce a finding:
    
    **Finding [N]:**
    - Standard: [NIST control ID / OWASP ASVS control / CWE ID]
    - SAST rule: [rule name or category]
    - Severity: Critical / High / Medium / Low
    - Line: [number or range]
    - Issue: [one sentence]
    - Fix: [one sentence — the required code change]
    
    ## NIST SP 800-53
    
    - AC-3 — Access enforcement: is an authorization check enforced before
      every data operation?
    - AC-6 — Least privilege: does the code request only minimum permissions?
    - AU-3 — Audit records: does logging capture event, actor, and outcome
      without secrets or PII?
    - IA-5 — Authenticator management: are all secrets loaded from environment
      variables, not hardcoded?
    - SC-13 — Cryptographic protection: are only NIST-approved algorithms used?
    - SI-10 — Input validation: is all input validated before processing?
    
    ## OWASP ASVS Level 1
    
    - V4.1.1 — Access control enforced server-side on every request
    - V4.2.1 — Object-level authorization checked — no IDOR via predictable IDs
    - V5.1.1 — String inputs define max_length constraints
    - V5.3.4 — No user input concatenated into query strings
    - V6.2.1 — No MD5, SHA-1, or custom cryptographic algorithms
    - V7.1.1 — Credentials and PII never written to logs
    - V7.4.1 — Error responses do not expose stack traces or internal details
    - V8.3.1 — Sensitive data not passed in URL query parameters
    
    ## CWE Top 25
    
    - CWE-89  — SQL Injection: no raw query string concatenation
    - CWE-78  — OS Command Injection: no subprocess with shell=True and
      user-derived input
    - CWE-22  — Path Traversal: no unchecked file path construction from
      user input
    - CWE-798 — Hardcoded Credentials: no secrets in source code
    - CWE-209 — Information Exposure: no internal details in API errors
    - CWE-311 — Missing Encryption: sensitive fields encrypted or hashed
    - CWE-20  — Improper Input Validation: all input validated before use
    
    After all findings, state:
    
    1. Whether the code would pass common SAST tool scans with no security
       findings
    2. Any remaining issues that would be flagged, with the exact rule name
    3. A one-sentence overall assessment
  16. Click Create.

    For scenarios where you do not have an existing skill, use Bob's /create-skill command for a guided setup.

    Tips for writing effective skills:

    • Keep skill instructions under approximately 2,000 words. Longer skills consume context that Bob needs for reading source code.
    • The user-invocable: true metadata in the front matter makes the skill visible and selectable in the Bob interface, so team members can activate it without writing a prompt from scratch.
    • Use explicit stop points like "return the compliance checklist when complete" to ensure Bob reports results before taking further action.
    • Skills complement project rules — rules prevent anti-patterns globally, while skills encode task-specific workflows.

Run the actor-critic workflow

With rules and skills in place, ask Bob to orchestrate the full actor-critic workflow. A single parent task spawns the Actor and Critic as independent subagents — the Actor writes the code, then the Critic reviews the code in an isolated context with no access to the Actor's reasoning.

The feature is a new GET /bookings/{booking_id} endpoint that returns booking details only to the booking's owner. It is a focused scope that exercises every interesting control: IDOR protection, identity verification, input validation, ORM-only queries, generic errors, and PII-free logging.

  1. Click the + button to start a new task.

    Starting a new task gives the actor-critic workflow a clean context window, separate from the rules and skill creation work done earlier.

  2. Click the mode menu in the chat panel and select Agent.

  3. Click Permissions in the chat panel and check Read, Edit, Execute, Skill, and Subagent. Leave all other toggles unchecked.

    PermissionStateWhy
    Read✅ OnBob and subagents read source files and generated output
    Edit✅ OnThe Actor subagent writes the new endpoint file
    Execute✅ OnBob may run shell commands to resolve paths or structure
    Skill✅ OnEnables the parent agent and the subagents it spawns to load and activate skills
    Subagent✅ OnRequired to spawn the Actor and Critic as independent subagents
    MCP❌ OffNot required for this task
  4. Ask Bob to orchestrate the actor-critic workflow.

    The @ context mentions attach three files from the Galaxium Travels backend so the Actor subagent understands the existing code conventions before writing the new endpoint: server.py is the FastAPI application entry point, booking.py is the booking service, and schemas.py defines the Pydantic request and response models.

    Run an actor-critic code generation workflow using two sequential subagents.
    
    Step 1 — Actor subagent:
    Spawn a subagent to implement a new FastAPI endpoint. Load the
    /secure-python-actor skill. Reference the following files:
    
    @booking_system_backend/server.py
    @booking_system_backend/services/booking.py
    @booking_system_backend/schemas.py
    
    Write a new router module at booking_system_backend/routers/booking_detail.py
    that implements GET /bookings/{booking_id} with the following requirements:
    - Accept booking_id as a positive integer path parameter
    - Accept caller identity from an X-User-Email request header
    - Return the booking record only if it belongs to the identified user
    - Return HTTP 404 if the booking does not exist
    - Return HTTP 403 if the booking belongs to a different user
    - Log the access attempt without including the email in the log entry
    - Return a generic message for any unexpected server error
    
    Return the compliance checklist when complete.
    
    Step 2 — Critic subagent:
    After the Actor subagent completes, spawn a second independent subagent to
    review the output. Load the /secure-python-critic skill. Reference:
    
    @booking_system_backend/routers/booking_detail.py
    
    Review the file against all controls in the skill. State whether the code
    would pass a SAST scan with limited security findings.
    
    Return the Critic's full findings report when complete.
  5. Click Approve todo tools for task when prompted. As each subagent starts you may see additional approval prompts — click Approve on each.

    Bob requests approval before spawning each subagent and before the Actor writes the endpoint file. Expect two to three prompts in total.

    Bob orchestrates both subagents sequentially. The Actor subagent reads the existing codebase, generates the endpoint, and returns a compliance checklist. The Critic subagent then receives only the generated file — not the Actor's context — and audits it independently.

    Example output:

    I have all the context needed. Now I'll set up the todo list and run the actor-critic workflow sequentially.
    
    Actor is done. Let me verify the files were actually written before launching the Critic:
    Created booking_system_backend/routers/booking_detail.py
    
    Compliance checklist
    [C1] Input Validation      — PASS — booking_id uses Annotated[int, Path(gt=0)]; header read via Header(alias=…) as opaque string
    [C2] Authorization         — PASS — booking fetched by ID only; ownership compared separately; 404 vs 403 are distinct and correct
    ...snip...
    
    The file is correct and complete. Now launch the Critic subagent:
    
    ---
    
    Starting Critic subagent...
    
    Critic subagent complete.
    Reviewed booking_system_backend/routers/booking_detail.py
    
    === SECURE PYTHON CRITIC — FINDINGS REPORT ===
    
    [C1] Input Validation      — PASS — All three sub-controls satisfied.
    • `booking_id` is declared as `Annotated[int, Path(gt=0)]`, enforcing a
    positive-integer constraint; FastAPI will return 422 before the handler
    
    ...snip...
    
    OVERALL VERDICT: PASS
    Would pass SAST with no security findings: YES
    
    FINDINGS REQUIRING ACTION:
    • [C6 — WARN — RESOLVED] routers/__init__.py exists and was confirmed
    present. No action required.

    The Critic's architectural note is not a code defect — it reflects the existing Galaxium Travels workflow.

  6. Open booking_system_backend/routers/booking_detail.py to review the generated code.

    The development team can now be confident that the new endpoint satisfies security standards before a human reviews it, and before it reaches a static analysis tool.

Clean up

  1. To remove the files created in this tutorial, delete the galaxium-travels directory cloned in Set up the lab.
  2. If you will no longer use the skills, click Bob - Settings >> Bob Settings then Skills.
  3. Click the secure-python-actor skill.
  4. Click the trash can icon to delete the skill, then click Delete.
  5. Repeat these steps to delete the secure-python-critic skill.

Next steps

In this tutorial, you used IBM Bob to:

  • Configure .bob/rules/security.md with Galaxium Travels security standards that Bob enforces on every task
  • Create an Actor skill that encodes NIST SP 800-53 and OWASP ASVS requirements as code generation instructions
  • Create a Critic skill that maps each control to common SAST rules
  • Orchestrate an actor-critic workflow where independent subagents generate and review code with no shared context
  • Produce a new FastAPI endpoint utilizing rules and skills to reduce security findings

Additional resources

How is this topic?