Skip to content

Commit

Permalink
feat: ensure emitters have "database" at least
Browse files Browse the repository at this point in the history
  • Loading branch information
ninoseki committed Jan 14, 2024
1 parent 88687ea commit 3d9ac01
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 101 deletions.
7 changes: 7 additions & 0 deletions lib/mihari/schemas/rule.rb
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,13 @@ class RuleContract < Dry::Validation::Contract
key.failure("#{v} is not a valid format.") unless valid_falsepositive?(v)
end
end

rule(:emitters) do
emitters = value.filter_map { |v| v[:emitter] }
unless emitters.include?(Mihari::Emitters::Database.key)
key.failure("Emitter:#{Mihari::Emitters::Database.key} should be included in emitters")
end
end
end
end
end
2 changes: 1 addition & 1 deletion spec/factories/rules.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
description: description,
queries: [],
tags: [],
emitters: []
emitters: [{ emitter: "database" }]
}
end

Expand Down
191 changes: 91 additions & 100 deletions spec/schemas/rule_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,167 +6,158 @@
let!(:description) { "test" }
let!(:title) { "test" }

let(:queries) { [] }
let(:emitters) { nil }
let(:enrichers) { nil }
let(:falsepositives) { nil }
let(:data_types) { nil }
let(:artifact_ttl) { nil }
let(:data) do
{
id: id,
description: description,
title: title,
queries: queries,
emitters: emitters,
enrichers: enrichers,
falsepositives: falsepositives,
data_types: data_types,
artifact_ttl: artifact_ttl
}.compact
end

context "with valid rule" do
it "has default values" do
result = contract.call(
id: id,
description: description,
title: title,
queries: [{ analyzer: "shodan", query: "foo" }]
)

result = contract.call(**data)
expect(result[:enrichers].length).to eq Mihari::DEFAULT_ENRICHERS.length
expect(result[:emitters].length).to eq Mihari::DEFAULT_EMITTERS.length
expect(result[:data_types].length).to eq Mihari::DEFAULT_DATA_TYPES.length
expect(result[:tags].length).to eq 0
end

context "with analyzers don't need additional options" do
it do
let(:queries) do
analyzers = Mihari.analyzer_to_class.keys - %w[zoomeye crtsh feed hunterhow]

analyzers.each do |analyzer|
result = contract.call(
id: id,
description: description,
title: title,
queries: [{ analyzer: analyzer, query: "foo" }]
)
expect(result.errors.empty?).to eq true
analyzers.map do |analyzer|
{ analyzer: analyzer, query: "foo" }
end
end

it do
result = contract.call(**data)
expect(result.errors.empty?).to eq true
end
end

context "with analyzers need additional options" do
let(:queries) do
[
{ analyzer: "crtsh", query: "foo", exclude_expired: true },
{ analyzer: "zoomeye", query: "foo", type: "host" },
{ analyzer: "zoomeye", query: "foo", type: "host", options: { interval: 10 } }
]
end

it do
result = contract.call(
id: id,
description: description,
title: title,
queries: [
{ analyzer: "crtsh", query: "foo", exclude_expired: true },
{ analyzer: "zoomeye", query: "foo", type: "host" },
{ analyzer: "zoomeye", query: "foo", type: "host", options: { interval: 10 } }
]
)
result = contract.call(**data)
expect(result.errors.empty?).to be true
end
end

context "with allowed_data_types" do
let(:data_types) { ["ip"] }

it do
result = contract.call(
id: id,
description: description,
title: title,
queries: [
{ analyzer: "shodan", query: "foo" }
],
data_types: ["ip"]
)
result = contract.call(**data)
expect(result.errors.empty?).to be true
end
end
end

context "with invalid analyzer name" do
let(:queries) { [{ analyzer: "foo", query: "foo" }] }

it do
result = contract.call(
id: id,
description: description,
title: title,
queries: [{ analyzer: "foo", query: "foo" }]
)
result = contract.call(**data)
expect(result.errors.empty?).to be false
end
end

context "with invalid options" do
let(:queries) do
[
{ analyzer: "shodan", query: "foo" },
{ analyzer: "crtsh", query: "foo", exclude_expired: 1 }, # should be bool
{ analyzer: "zoomeye", query: "foo", type: "bar" } # should be any of host or web
]
end

it do
result = contract.call(
id: id,
description: description,
title: title,
queries: [
{ analyzer: "shodan", query: "foo" },
{ analyzer: "crtsh", query: "foo", exclude_expired: 1 }, # should be bool
{ analyzer: "zoomeye", query: "foo", type: "bar" } # should be any of host or web
]
)
result = contract.call(**data)
expect(result.errors.empty?).to be false
end
end

context "with invalid data types" do
let(:data_types) do
# should be any of ip, domain, mail, url or hash
["foo"]
end

it do
result = contract.call(
id: id,
description: description,
title: title,
queries: [
{ analyzer: "shodan", query: "foo" }
],
data_types: ["foo"] # should be any of ip, domain, mail, url or hash
)
result = contract.call(**data)
expect(result.errors.empty?).to be false
end
end

context "with invalid disallowed data values" do
context "with non-string falsepositives values" do
let(:falsepositives) do
# should be string
[1]
end

it do
result = contract.call(
id: id,
description: description,
title: title,
queries: [
{ analyzer: "shodan", query: "foo" }
],
falsepositives: [1] # should be string
)
result = contract.call(**data)
expect(result.errors.empty?).to be false
end
end

context "with non-string falsepositives values" do
let(:falsepositives) do
# /*/ is not compilable as a regexp
["/*/"]
end

it do
result = contract.call(
id: id,
description: description,
title: title,
queries: [
{ analyzer: "shodan", query: "foo" }
],
falsepositives: ["/*/"]
)
result = contract.call(**data)
expect(result.errors.empty?).to be false
end
end

context "with invalid artifact_ttl" do
let(:artifact_ttl) { "foo " }

it do
result = contract.call(
id: id,
description: description,
title: title,
queries: [
{ analyzer: "shodan", query: "foo" }
],
artifact_ttl: "foo" # should be an integer
)
result = contract.call(**data)
expect(result.errors.empty?).to be false
end
end

context "with invalid emitter name" do
let(:emitters) { [{ emitter: "foo" }] }

it do
result = contract.call(
id: id,
description: description,
title: title,
queries: [
{ analyzer: "shodan", query: "foo" }
],
emitters: [
{ emitter: "foo" }
]
)
result = contract.call(**data)
expect(result.errors.empty?).to be false
end
end

context "without having database emitter" do
let(:emitters) { [{ emitter: "misp" }] }

it do
result = contract.call(**data)
expect(result.errors.to_h).to eq({ emitters: ["Emitter:database should be included in emitters"] })
end
end
end

0 comments on commit 3d9ac01

Please sign in to comment.