Class: Aidp::Security::WatchModeHandler

Inherits:
Object
  • Object
show all
Defined in:
lib/aidp/security/watch_mode_handler.rb

Overview

Handles security policy violations in watch mode with fail-forward logic When a Rule of Two violation occurs, attempts to find alternative approaches using AGD/ZFC before failing. If no path forward, adds a comment and label.

Fail-forward flow:

  1. Security violation detected

  2. Attempt to convert to compliant operation (up to max_retry_attempts)

    • Try using secrets proxy instead of direct credential access

    • Try sanitizing untrusted input

    • Try deferring egress operations

  3. If all attempts fail, add PR/issue comment and aidp-needs-input label

Constant Summary collapse

DEFAULT_MAX_RETRY_ATTEMPTS =
3
DEFAULT_NEEDS_INPUT_LABEL =
"aidp-needs-input"

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(repository_client:, config: {}) ⇒ WatchModeHandler

Returns a new instance of WatchModeHandler.



22
23
24
25
26
# File 'lib/aidp/security/watch_mode_handler.rb', line 22

def initialize(repository_client:, config: {})
  @repository_client = repository_client
  @config = normalize_config(config)
  @retry_counts = {}
end

Instance Attribute Details

#configObject (readonly)

Returns the value of attribute config.



20
21
22
# File 'lib/aidp/security/watch_mode_handler.rb', line 20

def config
  @config
end

#repository_clientObject (readonly)

Returns the value of attribute repository_client.



20
21
22
# File 'lib/aidp/security/watch_mode_handler.rb', line 20

def repository_client
  @repository_client
end

Instance Method Details

#enabled?Boolean

Check if security handling is enabled

Returns:

  • (Boolean)


101
102
103
# File 'lib/aidp/security/watch_mode_handler.rb', line 101

def enabled?
  @config[:fail_forward_enabled] != false
end

#handle_violation(violation, context:) ⇒ Hash

Handle a security policy violation

Parameters:

  • violation (PolicyViolation)

    The violation that occurred

  • context (Hash)

    Context about the operation

    • :issue_number or :pr_number - The issue/PR number

    • :work_unit_id - The work unit identifier

    • :operation - The operation that was attempted

Returns:

  • (Hash)

    Result of handling: { recovered: bool, action: symbol, message: string }



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
81
82
83
84
85
86
87
88
89
90
91
92
93
# File 'lib/aidp/security/watch_mode_handler.rb', line 35

def handle_violation(violation, context:)
  work_unit_id = context[:work_unit_id] || "unknown"
  issue_or_pr_number = context[:issue_number] || context[:pr_number]

  Aidp.log_debug("security.watch_handler", "handling_violation",
    work_unit_id: work_unit_id,
    flag: violation.flag,
    source: violation.source)

  # Increment retry count for this work unit
  @retry_counts[work_unit_id] ||= 0
  @retry_counts[work_unit_id] += 1
  retry_count = @retry_counts[work_unit_id]

  if retry_count <= max_retry_attempts
    # Attempt to find alternative approach
    result = attempt_fail_forward(violation, context, retry_count)

    if result[:recovered]
      Aidp.log_info("security.watch_handler", "violation_recovered",
        work_unit_id: work_unit_id,
        attempt: retry_count,
        strategy: result[:strategy])
      return result
    end

    # Not yet at max retries, will try again
    Aidp.log_debug("security.watch_handler", "fail_forward_attempt_failed",
      work_unit_id: work_unit_id,
      attempt: retry_count,
      max_attempts: max_retry_attempts)

    {
      recovered: false,
      action: :retry,
      message: "Security violation, attempting alternative approach (#{retry_count}/#{max_retry_attempts})",
      retry_count: retry_count
    }
  else
    # Max retries exceeded - add comment and label
    Aidp.log_warn("security.watch_handler", "max_retries_exceeded",
      work_unit_id: work_unit_id,
      retry_count: retry_count)

    if issue_or_pr_number
      add_security_comment_and_label(violation, context, issue_or_pr_number)
    end

    # Clear retry count
    @retry_counts.delete(work_unit_id)

    {
      recovered: false,
      action: :fail,
      message: "Security policy violation cannot be resolved automatically. Manual intervention required.",
      needs_input: true
    }
  end
end

#reset_retry_count(work_unit_id) ⇒ Object

Reset retry count for a work unit (call on success or explicit reset)



96
97
98
# File 'lib/aidp/security/watch_mode_handler.rb', line 96

def reset_retry_count(work_unit_id)
  @retry_counts.delete(work_unit_id)
end