Class: Aidp::Firewall::ProviderRequirementsCollector

Inherits:
Object
  • Object
show all
Defined in:
lib/aidp/firewall/provider_requirements_collector.rb

Overview

Collects firewall requirements from all provider classes and generates the firewall configuration YAML file

Constant Summary collapse

CORE_DOMAINS =

Core infrastructure domains not tied to specific providers

{
  ruby: [
    "rubygems.org",
    "api.rubygems.org",
    "index.rubygems.org"
  ],
  javascript: [
    "registry.npmjs.org",
    "registry.yarnpkg.com"
  ],
  github: [
    "github.com",
    "api.github.com",
    "raw.githubusercontent.com",
    "objects.githubusercontent.com",
    "gist.githubusercontent.com",
    "cloud.githubusercontent.com"
  ],
  cdn: [
    "cdn.jsdelivr.net"
  ],
  vscode: [
    "update.code.visualstudio.com",
    "marketplace.visualstudio.com",
    "vscode.blob.core.windows.net",
    "vscode.download.prss.microsoft.com",
    "az764295.vo.msecnd.net",
    "gallerycdn.vsassets.io",
    "vscode.gallerycdn.vsassets.io",
    "gallery.vsassets.io",
    "vscode-sync.trafficmanager.net",
    "vscode.dev",
    "go.microsoft.com",
    "download.visualstudio.microsoft.com"
  ],
  telemetry: [
    "dc.services.visualstudio.com",
    "vortex.data.microsoft.com"
  ]
}.freeze
STATIC_IP_RANGES =

Static IP ranges (CIDR notation)

[
  {"cidr" => "140.82.112.0/20", "comment" => "GitHub main infrastructure (covers 140.82.112.0 - 140.82.127.255)"},
  {"cidr" => "127.0.0.0/8", "comment" => "Localhost loopback"}
].freeze
AZURE_IP_RANGES =

Azure IP ranges for GitHub Copilot and VS Code services These use broader /16 ranges to handle dynamic IP allocation across Azure regions

[
  {"cidr" => "20.189.0.0/16", "comment" => "Azure WestUS2 (GitHub Copilot - broad range due to dynamic IP allocation)"},
  {"cidr" => "104.208.0.0/16", "comment" => "Azure EastUS (GitHub Copilot - broad range due to dynamic IP allocation)"},
  {"cidr" => "52.168.0.0/16", "comment" => "Azure EastUS2 (GitHub Copilot - covers .112 and .117, broad range)"},
  {"cidr" => "40.79.0.0/16", "comment" => "Azure WestUS (GitHub Copilot - broad range due to dynamic IP allocation)"},
  {"cidr" => "13.89.0.0/16", "comment" => "Azure EastUS (GitHub Copilot - broad range due to dynamic IP allocation)"},
  {"cidr" => "13.69.0.0/16", "comment" => "Azure (covers .239, broad range due to dynamic IP allocation)"},
  {"cidr" => "13.66.0.0/16", "comment" => "Azure WestUS2 (VS Code sync service - broad range)"},
  {"cidr" => "20.42.0.0/16", "comment" => "Azure WestEurope (covers .65 and .73, broad range)"},
  {"cidr" => "20.50.0.0/16", "comment" => "Azure (covers .80, broad range due to dynamic IP allocation)"}
].freeze
DYNAMIC_SOURCES =

Dynamic IP sources configuration

{
  github_meta_api: {
    url: "https://api.github.com/meta",
    fields: ["git"],
    comment: "GitHub Git protocol IP ranges (dynamically fetched)"
  }
}.freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(config_path: nil) ⇒ ProviderRequirementsCollector

Returns a new instance of ProviderRequirementsCollector.



93
94
95
# File 'lib/aidp/firewall/provider_requirements_collector.rb', line 93

def initialize(config_path: nil)
  @config_path = config_path || default_config_path
end

Instance Attribute Details

#config_pathObject (readonly)

Returns the value of attribute config_path.



20
21
22
# File 'lib/aidp/firewall/provider_requirements_collector.rb', line 20

def config_path
  @config_path
end

Instance Method Details

#collect_requirementsHash

Collect firewall requirements from all providers

Returns:

  • (Hash)

    Hash with provider names as keys and requirements as values



100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
# File 'lib/aidp/firewall/provider_requirements_collector.rb', line 100

def collect_requirements
  Aidp.log_debug("firewall_collector", "collecting requirements", provider_count: provider_classes.size)

  requirements = {}
  provider_classes.each do |provider_class|
    provider_name = extract_provider_name(provider_class)
    reqs = provider_class.firewall_requirements

    requirements[provider_name] = {
      domains: reqs[:domains] || [],
      ip_ranges: reqs[:ip_ranges] || []
    }

    Aidp.log_debug(
      "firewall_collector",
      "collected requirements",
      provider: provider_name,
      domains: reqs[:domains]&.size || 0,
      ip_ranges: reqs[:ip_ranges]&.size || 0
    )
  end

  requirements
end

#deduplicate(requirements) ⇒ Hash

Deduplicate and merge provider requirements

Parameters:

  • requirements (Hash)

    Provider requirements hash

Returns:

  • (Hash)

    Deduplicated requirements with all_domains and all_ip_ranges



129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
# File 'lib/aidp/firewall/provider_requirements_collector.rb', line 129

def deduplicate(requirements)
  all_domains = []
  all_ip_ranges = []

  requirements.each_value do |reqs|
    all_domains.concat(reqs[:domains])
    all_ip_ranges.concat(reqs[:ip_ranges])
  end

  {
    all_domains: all_domains.uniq.sort,
    all_ip_ranges: all_ip_ranges.uniq.sort,
    by_provider: requirements
  }
end

#generate_reportString

Generate a summary report of provider requirements

Returns:

  • (String)

    Formatted summary report



205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
# File 'lib/aidp/firewall/provider_requirements_collector.rb', line 205

def generate_report
  requirements = collect_requirements
  deduplicated = deduplicate(requirements)

  report = []
  report << "Firewall Provider Requirements Summary"
  report << "=" * 50
  report << ""
  report << "Total Providers: #{requirements.size}"
  report << "Total Unique Domains: #{deduplicated[:all_domains].size}"
  report << "Total Unique IP Ranges: #{deduplicated[:all_ip_ranges].size}"
  report << ""
  report << "By Provider:"
  report << "-" * 50

  requirements.each do |provider, reqs|
    report << ""
    report << "#{provider.capitalize}:"
    report << "  Domains (#{reqs[:domains].size}):"
    reqs[:domains].each { |d| report << "    - #{d}" }
    if reqs[:ip_ranges].any?
      report << "  IP Ranges (#{reqs[:ip_ranges].size}):"
      reqs[:ip_ranges].each { |ip| report << "    - #{ip}" }
    end
  end

  report.join("\n")
end

#generate_yaml_config(dry_run: false) ⇒ Boolean Also known as: update_yaml_config

Generate the complete YAML configuration file

Creates a new YAML file with core infrastructure and provider requirements

Parameters:

  • dry_run (Boolean) (defaults to: false)

    If true, only log what would be generated

Returns:

  • (Boolean)

    True if generation was successful



151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
# File 'lib/aidp/firewall/provider_requirements_collector.rb', line 151

def generate_yaml_config(dry_run: false)
  Aidp.log_info("firewall_collector", "generating config", path: @config_path, dry_run: dry_run)

  # Collect provider requirements
  provider_requirements = collect_requirements
  deduplicated = deduplicate(provider_requirements)

  # Build complete configuration
  config = {
    "version" => 1,
    "static_ip_ranges" => STATIC_IP_RANGES,
    "azure_ip_ranges" => AZURE_IP_RANGES,
    "core_domains" => CORE_DOMAINS.transform_keys(&:to_s),
    "provider_domains" => provider_requirements.transform_values { |reqs| reqs[:domains] },
    "dynamic_sources" => DYNAMIC_SOURCES.transform_keys(&:to_s).transform_values do |v|
      v.transform_keys(&:to_s)
    end
  }

  if dry_run
    Aidp.log_info("firewall_collector", "dry run - would generate", domains: deduplicated[:all_domains].size)
    puts "Would generate YAML with:"
    puts "  - #{STATIC_IP_RANGES.size} static IP ranges"
    puts "  - #{AZURE_IP_RANGES.size} Azure IP ranges"
    puts "  - #{CORE_DOMAINS.values.flatten.size} core domains"
    puts "  - #{deduplicated[:all_domains].size} provider domains from #{provider_requirements.size} providers"
    return true
  end

  # Ensure directory exists
  FileUtils.mkdir_p(File.dirname(@config_path))

  # Write configuration
  File.write(@config_path, YAML.dump(config))
  Aidp.log_info(
    "firewall_collector",
    "config generated",
    path: @config_path,
    providers: provider_requirements.size,
    total_domains: CORE_DOMAINS.values.flatten.size + deduplicated[:all_domains].size
  )

  true
rescue => e
  Aidp.log_error("firewall_collector", "generation failed", error: e.message)
  false
end