mirror of
https://github.com/falcosecurity/falco.git
synced 2025-10-22 20:29:39 +00:00
Related to the changes in https://github.com/draios/sysdig/pull/1501, add support for an "intersects" operator that verifies if any of the values in the rhs of an expression are found in the set of extracted values. For example: (a,b,c) in (a,b) is false, but (a,b,c) intersects (a,b) is true. The code that implements CO_INTERSECTS is in a different commit. Signed-off-by: Mark Stemm <mark.stemm@gmail.com>
200 lines
6.1 KiB
Lua
200 lines
6.1 KiB
Lua
-- Copyright (C) 2019 The Falco Authors.
|
|
--
|
|
-- Licensed under the Apache License, Version 2.0 (the "License");
|
|
-- you may not use this file except in compliance with the License.
|
|
-- You may obtain a copy of the License at
|
|
--
|
|
-- http://www.apache.org/licenses/LICENSE-2.0
|
|
--
|
|
-- Unless required by applicable law or agreed to in writing, software
|
|
-- distributed under the License is distributed on an "AS IS" BASIS,
|
|
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
-- See the License for the specific language governing permissions and
|
|
-- limitations under the License.
|
|
--
|
|
|
|
local parser = require("parser")
|
|
local sinsp_rule_utils = {}
|
|
|
|
function sinsp_rule_utils.check_for_ignored_syscalls_events(ast, filter_type, source)
|
|
|
|
function check_syscall(val)
|
|
if ignored_syscalls[val] then
|
|
error("Ignored syscall \""..val.."\" in "..filter_type..": "..source)
|
|
end
|
|
|
|
end
|
|
|
|
function check_event(val)
|
|
if ignored_events[val] then
|
|
error("Ignored event \""..val.."\" in "..filter_type..": "..source)
|
|
end
|
|
end
|
|
|
|
function cb(node)
|
|
if node.left.type == "FieldName" and
|
|
(node.left.value == "evt.type" or
|
|
node.left.value == "syscall.type") then
|
|
|
|
if (node.operator == "in" or
|
|
node.operator == "intersects" or
|
|
node.operator == "pmatch") then
|
|
for i, v in ipairs(node.right.elements) do
|
|
if v.type == "BareString" then
|
|
if node.left.value == "evt.type" then
|
|
check_event(v.value)
|
|
else
|
|
check_syscall(v.value)
|
|
end
|
|
end
|
|
end
|
|
else
|
|
if node.right.type == "BareString" then
|
|
if node.left.value == "evt.type" then
|
|
check_event(node.right.value)
|
|
else
|
|
check_syscall(node.right.value)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
parser.traverse_ast(ast, {BinaryRelOp=1}, cb)
|
|
end
|
|
|
|
-- Examine the ast and find the event types/syscalls for which the
|
|
-- rule should run. All evt.type references are added as event types
|
|
-- up until the first "!=" binary operator or unary not operator. If
|
|
-- no event type checks are found afterward in the rule, the rule is
|
|
-- considered optimized and is associated with the event type(s).
|
|
--
|
|
-- Otherwise, the rule is associated with a 'catchall' category and is
|
|
-- run for all event types/syscalls. (Also, a warning is printed).
|
|
--
|
|
|
|
function sinsp_rule_utils.get_evttypes_syscalls(name, ast, source, warn_evttypes, verbose)
|
|
|
|
local evttypes = {}
|
|
local syscallnums = {}
|
|
local evtnames = {}
|
|
local found_event = false
|
|
local found_not = false
|
|
local found_event_after_not = false
|
|
|
|
function cb(node)
|
|
if node.type == "UnaryBoolOp" then
|
|
if node.operator == "not" then
|
|
found_not = true
|
|
end
|
|
else
|
|
if node.operator == "!=" then
|
|
found_not = true
|
|
end
|
|
if node.left.type == "FieldName" and node.left.value == "evt.type" then
|
|
found_event = true
|
|
if found_not then
|
|
found_event_after_not = true
|
|
end
|
|
if (node.operator == "in" or
|
|
node.operator == "intersects" or
|
|
node.operator == "pmatch") then
|
|
for i, v in ipairs(node.right.elements) do
|
|
if v.type == "BareString" then
|
|
|
|
-- The event must be a known event
|
|
if events[v.value] == nil and syscalls[v.value] == nil then
|
|
error("Unknown event/syscall \""..v.value.."\" in filter: "..source)
|
|
end
|
|
|
|
evtnames[v.value] = 1
|
|
if events[v.value] ~= nil then
|
|
for id in string.gmatch(events[v.value], "%S+") do
|
|
evttypes[id] = 1
|
|
end
|
|
end
|
|
|
|
if syscalls[v.value] ~= nil then
|
|
for id in string.gmatch(syscalls[v.value], "%S+") do
|
|
syscallnums[id] = 1
|
|
end
|
|
end
|
|
end
|
|
end
|
|
else
|
|
if node.right.type == "BareString" then
|
|
|
|
-- The event must be a known event
|
|
if events[node.right.value] == nil and syscalls[node.right.value] == nil then
|
|
error("Unknown event/syscall \""..node.right.value.."\" in filter: "..source)
|
|
end
|
|
|
|
evtnames[node.right.value] = 1
|
|
if events[node.right.value] ~= nil then
|
|
for id in string.gmatch(events[node.right.value], "%S+") do
|
|
evttypes[id] = 1
|
|
end
|
|
end
|
|
|
|
if syscalls[node.right.value] ~= nil then
|
|
for id in string.gmatch(syscalls[node.right.value], "%S+") do
|
|
syscallnums[id] = 1
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
parser.traverse_ast(ast.filter.value, {BinaryRelOp=1, UnaryBoolOp=1} , cb)
|
|
|
|
if not found_event then
|
|
if warn_evttypes == true then
|
|
io.stderr:write("Rule "..name..": warning (no-evttype):\n")
|
|
io.stderr:write(source.."\n")
|
|
io.stderr:write(" did not contain any evt.type restriction, meaning it will run for all event types.\n")
|
|
io.stderr:write(" This has a significant performance penalty. Consider adding an evt.type restriction if possible.\n")
|
|
end
|
|
evttypes = {}
|
|
syscallnums = {}
|
|
evtnames = {}
|
|
end
|
|
|
|
if found_event_after_not then
|
|
if warn_evttypes == true then
|
|
io.stderr:write("Rule "..name..": warning (trailing-evttype):\n")
|
|
io.stderr:write(source.."\n")
|
|
io.stderr:write(" does not have all evt.type restrictions at the beginning of the condition,\n")
|
|
io.stderr:write(" or uses a negative match (i.e. \"not\"/\"!=\") for some evt.type restriction.\n")
|
|
io.stderr:write(" This has a performance penalty, as the rule can not be limited to specific event types.\n")
|
|
io.stderr:write(" Consider moving all evt.type restrictions to the beginning of the rule and/or\n")
|
|
io.stderr:write(" replacing negative matches with positive matches if possible.\n")
|
|
end
|
|
evttypes = {}
|
|
syscallnums = {}
|
|
evtnames = {}
|
|
end
|
|
|
|
evtnames_only = {}
|
|
local num_evtnames = 0
|
|
for name, dummy in pairs(evtnames) do
|
|
table.insert(evtnames_only, name)
|
|
num_evtnames = num_evtnames + 1
|
|
end
|
|
|
|
if num_evtnames == 0 then
|
|
table.insert(evtnames_only, "all")
|
|
end
|
|
|
|
table.sort(evtnames_only)
|
|
|
|
if verbose then
|
|
io.stderr:write("Event types/Syscalls for rule "..name..": "..table.concat(evtnames_only, ",").."\n")
|
|
end
|
|
|
|
return evttypes, syscallnums
|
|
end
|
|
|
|
return sinsp_rule_utils
|