475 lines
14 KiB
Ruby
475 lines
14 KiB
Ruby
require "test_helper"
|
|
|
|
class WafPolicyTest < ActiveSupport::TestCase
|
|
setup do
|
|
@user = users(:jason)
|
|
@policy = WafPolicy.new(
|
|
name: "Block Malicious IPs",
|
|
policy_type: "country",
|
|
targets: ["BR", "CN"],
|
|
policy_action: "deny",
|
|
user: @user
|
|
)
|
|
end
|
|
|
|
# Validations
|
|
test "should be valid with all required attributes" do
|
|
assert @policy.valid?
|
|
end
|
|
|
|
test "should not be valid without name" do
|
|
@policy.name = nil
|
|
assert_not @policy.valid?
|
|
assert_includes @policy.errors[:name], "can't be blank"
|
|
end
|
|
|
|
test "should not be valid without unique name" do
|
|
@policy.name = waf_policies(:one).name
|
|
assert_not @policy.valid?
|
|
assert_includes @policy.errors[:name], "has already been taken"
|
|
end
|
|
|
|
test "should validate policy_type inclusion" do
|
|
@policy.policy_type = "invalid_type"
|
|
assert_not @policy.valid?
|
|
assert_includes @policy.errors[:policy_type], "is not included in the list"
|
|
end
|
|
|
|
test "should validate policy_action inclusion" do
|
|
@policy.policy_action = "invalid_action"
|
|
assert_not @policy.valid?
|
|
assert_includes @policy.errors[:policy_action], "is not included in the list"
|
|
end
|
|
|
|
test "should not be valid without targets" do
|
|
@policy.targets = []
|
|
assert_not @policy.valid?
|
|
assert_includes @policy.errors[:targets], "can't be blank"
|
|
end
|
|
|
|
test "should validate targets is an array" do
|
|
@policy.targets = "not an array"
|
|
assert_not @policy.valid?
|
|
assert_includes @policy.errors[:targets], "must be an array"
|
|
end
|
|
|
|
test "should validate country targets format" do
|
|
@policy.policy_type = "country"
|
|
|
|
# Valid country codes
|
|
@policy.targets = ["US", "BR", "CN"]
|
|
assert @policy.valid?
|
|
|
|
# Invalid country codes
|
|
@policy.targets = ["USA", "123", "B"]
|
|
assert_not @policy.valid?
|
|
assert_includes @policy.errors[:targets], "must be valid ISO country codes"
|
|
end
|
|
|
|
test "should validate ASN targets format" do
|
|
@policy.policy_type = "asn"
|
|
|
|
# Valid ASNs
|
|
@policy.targets = [12345, 67890]
|
|
assert @policy.valid?
|
|
|
|
# Invalid ASNs
|
|
@policy.targets = ["AS12345", -1, 0]
|
|
assert_not @policy.valid?
|
|
assert_includes @policy.errors[:targets], "must be valid ASNs"
|
|
end
|
|
|
|
test "should validate company targets format" do
|
|
@policy.policy_type = "company"
|
|
|
|
# Valid company names
|
|
@policy.targets = ["Google", "Amazon Web Services", "Microsoft"]
|
|
assert @policy.valid?
|
|
|
|
# Invalid company names
|
|
@policy.targets = ["", nil, " "]
|
|
assert_not @policy.valid?
|
|
assert_includes @policy.errors[:targets], "must be valid company names"
|
|
end
|
|
|
|
test "should validate network_type targets format" do
|
|
@policy.policy_type = "network_type"
|
|
|
|
# Valid network types
|
|
@policy.targets = ["datacenter", "proxy", "vpn", "standard"]
|
|
assert @policy.valid?
|
|
|
|
# Invalid network types
|
|
@policy.targets = ["invalid", "malicious", "botnet"]
|
|
assert_not @policy.valid?
|
|
assert_includes @policy.errors[:targets], "must be one of: datacenter, proxy, vpn, standard"
|
|
end
|
|
|
|
test "should validate redirect configuration" do
|
|
@policy.policy_action = "redirect"
|
|
|
|
# Valid redirect config
|
|
@policy.additional_data = { "redirect_url" => "https://example.com/blocked" }
|
|
assert @policy.valid?
|
|
|
|
# Missing redirect URL
|
|
@policy.additional_data = { "other_config" => "value" }
|
|
assert_not @policy.valid?
|
|
assert_includes @policy.errors[:additional_data], "must include 'redirect_url' for redirect action"
|
|
end
|
|
|
|
test "should validate challenge configuration" do
|
|
@policy.policy_action = "challenge"
|
|
|
|
# Valid challenge types
|
|
@policy.additional_data = { "challenge_type" => "captcha" }
|
|
assert @policy.valid?
|
|
|
|
@policy.additional_data = { "challenge_type" => "javascript" }
|
|
assert @policy.valid?
|
|
|
|
# Invalid challenge type
|
|
@policy.additional_data = { "challenge_type" => "invalid" }
|
|
assert_not @policy.valid?
|
|
assert_includes @policy.errors[:additional_data], "challenge_type must be one of: captcha, javascript, proof_of_work"
|
|
|
|
# No challenge type (should be valid, uses defaults)
|
|
@policy.additional_data = {}
|
|
assert @policy.valid?
|
|
end
|
|
|
|
# Defaults and Callbacks
|
|
test "should default to enabled" do
|
|
@policy.enabled = nil
|
|
@policy.save!
|
|
assert @policy.enabled?
|
|
end
|
|
|
|
test "should default targets to empty array" do
|
|
policy = WafPolicy.new(
|
|
name: "Test Policy",
|
|
policy_type: "country",
|
|
policy_action: "deny",
|
|
user: @user
|
|
)
|
|
# before_validation should set defaults
|
|
policy.valid?
|
|
assert_equal [], policy.targets
|
|
end
|
|
|
|
test "should default additional_data to empty hash" do
|
|
policy = WafPolicy.new(
|
|
name: "Test Policy",
|
|
policy_type: "country",
|
|
targets: ["US"],
|
|
policy_action: "deny",
|
|
user: @user
|
|
)
|
|
policy.valid?
|
|
assert_equal({}, policy.additional_data)
|
|
end
|
|
|
|
# Policy Type Methods
|
|
test "policy type predicate methods work correctly" do
|
|
country_policy = WafPolicy.new(policy_type: "country")
|
|
assert country_policy.country_policy?
|
|
assert_not country_policy.asn_policy?
|
|
assert_not country_policy.company_policy?
|
|
assert_not country_policy.network_type_policy?
|
|
|
|
asn_policy = WafPolicy.new(policy_type: "asn")
|
|
assert_not asn_policy.country_policy?
|
|
assert asn_policy.asn_policy?
|
|
assert_not asn_policy.company_policy?
|
|
assert_not asn_policy.network_type_policy?
|
|
|
|
company_policy = WafPolicy.new(policy_type: "company")
|
|
assert_not company_policy.country_policy?
|
|
assert_not company_policy.asn_policy?
|
|
assert company_policy.company_policy?
|
|
assert_not company_policy.network_type_policy?
|
|
|
|
network_type_policy = WafPolicy.new(policy_type: "network_type")
|
|
assert_not network_type_policy.country_policy?
|
|
assert_not network_type_policy.asn_policy?
|
|
assert_not network_type_policy.company_policy?
|
|
assert network_type_policy.network_type_policy?
|
|
end
|
|
|
|
# Action Methods
|
|
test "action predicate methods work correctly" do
|
|
allow_policy = WafPolicy.new(policy_action: "allow")
|
|
assert allow_policy.allow_action?
|
|
assert_not allow_policy.deny_action?
|
|
assert_not allow_policy.redirect_action?
|
|
assert_not allow_policy.challenge_action?
|
|
|
|
deny_policy = WafPolicy.new(policy_action: "deny")
|
|
assert_not deny_policy.allow_action?
|
|
assert deny_policy.deny_action?
|
|
assert_not deny_policy.redirect_action?
|
|
assert_not deny_policy.challenge_action?
|
|
|
|
redirect_policy = WafPolicy.new(policy_action: "redirect")
|
|
assert_not redirect_policy.allow_action?
|
|
assert_not redirect_policy.deny_action?
|
|
assert redirect_policy.redirect_action?
|
|
assert_not redirect_policy.challenge_action?
|
|
|
|
challenge_policy = WafPolicy.new(policy_action: "challenge")
|
|
assert_not challenge_policy.allow_action?
|
|
assert_not challenge_policy.deny_action?
|
|
assert_not challenge_policy.redirect_action?
|
|
assert challenge_policy.challenge_action?
|
|
end
|
|
|
|
# Policy action methods (to avoid Rails conflicts)
|
|
test "policy action predicate methods work correctly" do
|
|
policy = WafPolicy.new(policy_action: "deny")
|
|
assert policy.deny_policy_action?
|
|
assert_not policy.allow_policy_action?
|
|
assert_not policy.redirect_policy_action?
|
|
assert_not policy.challenge_policy_action?
|
|
end
|
|
|
|
# Lifecycle Methods
|
|
test "active? works correctly" do
|
|
# Active policy
|
|
active_policy = WafPolicy.new(enabled: true, expires_at: nil)
|
|
assert active_policy.active?
|
|
|
|
# Enabled but expired
|
|
expired_policy = WafPolicy.new(enabled: true, expires_at: 1.day.ago)
|
|
assert_not expired_policy.active?
|
|
|
|
# Disabled with future expiration
|
|
disabled_policy = WafPolicy.new(enabled: false, expires_at: 1.day.from_now)
|
|
assert_not disabled_policy.active?
|
|
|
|
# Disabled with no expiration
|
|
disabled_no_exp = WafPolicy.new(enabled: false, expires_at: nil)
|
|
assert_not disabled_no_exp.active?
|
|
|
|
# Enabled with future expiration
|
|
future_exp = WafPolicy.new(enabled: true, expires_at: 1.day.from_now)
|
|
assert future_exp.active?
|
|
end
|
|
|
|
test "expired? works correctly" do
|
|
assert_not WafPolicy.new(expires_at: nil).expired?
|
|
assert_not WafPolicy.new(expires_at: 1.day.from_now).expired?
|
|
assert WafPolicy.new(expires_at: 1.day.ago).expired?
|
|
assert WafPolicy.new(expires_at: Time.current).expired?
|
|
end
|
|
|
|
test "activate! enables policy" do
|
|
@policy.enabled = false
|
|
@policy.save!
|
|
|
|
@policy.activate!
|
|
assert @policy.reload.enabled?
|
|
end
|
|
|
|
test "deactivate! disables policy" do
|
|
@policy.enabled = true
|
|
@policy.save!
|
|
|
|
@policy.deactivate!
|
|
assert_not @policy.reload.enabled?
|
|
end
|
|
|
|
test "expire! sets expiration to now" do
|
|
@policy.expire!
|
|
assert @policy.reload.expires_at <= Time.current
|
|
end
|
|
|
|
# Scopes
|
|
test "enabled scope returns only enabled policies" do
|
|
enabled_policy = WafPolicy.create!(
|
|
name: "Enabled Policy",
|
|
policy_type: "country",
|
|
targets: ["US"],
|
|
policy_action: "deny",
|
|
user: @user,
|
|
enabled: true
|
|
)
|
|
disabled_policy = WafPolicy.create!(
|
|
name: "Disabled Policy",
|
|
policy_type: "country",
|
|
targets: ["US"],
|
|
policy_action: "deny",
|
|
user: @user,
|
|
enabled: false
|
|
)
|
|
|
|
enabled_policies = WafPolicy.enabled
|
|
assert_includes enabled_policies, enabled_policy
|
|
assert_not_includes enabled_policies, disabled_policy
|
|
end
|
|
|
|
test "active scope returns only active policies" do
|
|
active_policy = WafPolicy.create!(
|
|
name: "Active Policy",
|
|
policy_type: "country",
|
|
targets: ["US"],
|
|
policy_action: "deny",
|
|
user: @user,
|
|
enabled: true,
|
|
expires_at: 1.day.from_now
|
|
)
|
|
expired_policy = WafPolicy.create!(
|
|
name: "Expired Policy",
|
|
policy_type: "country",
|
|
targets: ["US"],
|
|
policy_action: "deny",
|
|
user: @user,
|
|
enabled: true,
|
|
expires_at: 1.day.ago
|
|
)
|
|
disabled_policy = WafPolicy.create!(
|
|
name: "Disabled Policy",
|
|
policy_type: "country",
|
|
targets: ["US"],
|
|
policy_action: "deny",
|
|
user: @user,
|
|
enabled: false
|
|
)
|
|
|
|
active_policies = WafPolicy.active
|
|
assert_includes active_policies, active_policy
|
|
assert_not_includes active_policies, expired_policy
|
|
assert_not_includes active_policies, disabled_policy
|
|
end
|
|
|
|
# Class Factory Methods
|
|
test "create_country_policy works correctly" do
|
|
policy = WafPolicy.create_country_policy(
|
|
["US", "CA"],
|
|
policy_action: "deny",
|
|
user: @user,
|
|
name: "Custom Name"
|
|
)
|
|
|
|
assert policy.persisted?
|
|
assert_equal "Custom Name", policy.name
|
|
assert_equal "country", policy.policy_type
|
|
assert_equal "deny", policy.policy_action
|
|
assert_equal ["US", "CA"], policy.targets
|
|
assert_equal @user, policy.user
|
|
end
|
|
|
|
test "create_asn_policy works correctly" do
|
|
policy = WafPolicy.create_asn_policy(
|
|
[12345, 67890],
|
|
policy_action: "challenge",
|
|
user: @user
|
|
)
|
|
|
|
assert policy.persisted?
|
|
assert_equal "challenge ASNs 12345, 67890", policy.name
|
|
assert_equal "asn", policy.policy_type
|
|
assert_equal "challenge", policy.policy_action
|
|
assert_equal [12345, 67890], policy.targets
|
|
end
|
|
|
|
test "create_company_policy works correctly" do
|
|
policy = WafPolicy.create_company_policy(
|
|
["Google", "Amazon"],
|
|
policy_action: "deny",
|
|
user: @user
|
|
)
|
|
|
|
assert policy.persisted?
|
|
assert_equal "deny Google, Amazon", policy.name
|
|
assert_equal "company", policy.policy_type
|
|
assert_equal ["Google", "Amazon"], policy.targets
|
|
end
|
|
|
|
test "create_network_type_policy works correctly" do
|
|
policy = WafPolicy.create_network_type_policy(
|
|
["datacenter", "proxy"],
|
|
policy_action: "redirect",
|
|
user: @user,
|
|
additional_data: { redirect_url: "https://example.com/blocked" }
|
|
)
|
|
|
|
assert policy.persisted?
|
|
assert_equal "redirect datacenter, proxy", policy.name
|
|
assert_equal "network_type", policy.policy_type
|
|
assert_equal ["datacenter", "proxy"], policy.targets
|
|
end
|
|
|
|
# Redirect/Challenge Specific Methods
|
|
test "redirect_url and redirect_status methods work" do
|
|
policy = WafPolicy.new(
|
|
policy_action: "redirect",
|
|
additional_data: {
|
|
"redirect_url" => "https://example.com/blocked",
|
|
"redirect_status" => 301
|
|
}
|
|
)
|
|
|
|
assert_equal "https://example.com/blocked", policy.redirect_url
|
|
assert_equal 301, policy.redirect_status
|
|
|
|
# Default status
|
|
policy.additional_data = { "redirect_url" => "https://example.com/blocked" }
|
|
assert_equal 302, policy.redirect_status
|
|
end
|
|
|
|
test "challenge_type and challenge_message methods work" do
|
|
policy = WafPolicy.new(
|
|
policy_action: "challenge",
|
|
additional_data: {
|
|
"challenge_type" => "javascript",
|
|
"challenge_message" => "Please verify you are human"
|
|
}
|
|
)
|
|
|
|
assert_equal "javascript", policy.challenge_type
|
|
assert_equal "Please verify you are human", policy.challenge_message
|
|
|
|
# Default challenge type
|
|
policy.additional_data = {}
|
|
assert_equal "captcha", policy.challenge_type
|
|
end
|
|
|
|
# Statistics
|
|
test "generated_rules_count works" do
|
|
@policy.save!
|
|
|
|
# Initially no rules
|
|
assert_equal 0, @policy.generated_rules_count
|
|
|
|
# Create some rules
|
|
network_range = NetworkRange.create!(ip_range: "192.168.1.0/24")
|
|
@policy.create_rule_for_network_range(network_range)
|
|
|
|
assert_equal 1, @policy.generated_rules_count
|
|
end
|
|
|
|
test "effectiveness_stats returns correct data" do
|
|
@policy.save!
|
|
|
|
stats = @policy.effectiveness_stats
|
|
|
|
assert_equal 0, stats[:total_rules_generated]
|
|
assert_equal 0, stats[:active_rules]
|
|
assert_equal 0, stats[:rules_last_7_days]
|
|
assert_equal "country", stats[:policy_type]
|
|
assert_equal "deny", stats[:policy_action]
|
|
assert_equal 2, stats[:targets_count]
|
|
end
|
|
|
|
# String representations
|
|
test "to_s returns name" do
|
|
assert_equal @policy.name, @policy.to_s
|
|
end
|
|
|
|
test "to_param parameterizes name" do
|
|
@policy.name = "Block Brazil & China"
|
|
expected = "block-brazil-china"
|
|
assert_equal expected, @policy.to_param
|
|
end
|
|
end
|