mirror of
https://github.com/falcosecurity/falco.git
synced 2026-03-31 00:53:03 +00:00
Compare commits
5 Commits
embed-lua-
...
fix/1272
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7ab327749f | ||
|
|
4450fd3c4c | ||
|
|
5cca1a6589 | ||
|
|
130126f170 | ||
|
|
c886debf83 |
File diff suppressed because it is too large
Load Diff
@@ -44,8 +44,16 @@
|
|||||||
items: ["vpa-recommender", "vpa-updater"]
|
items: ["vpa-recommender", "vpa-updater"]
|
||||||
|
|
||||||
- list: allowed_k8s_users
|
- list: allowed_k8s_users
|
||||||
items: [
|
items:
|
||||||
"minikube", "minikube-user", "kubelet", "kops", "admin", "kube", "kube-proxy", "kube-apiserver-healthcheck",
|
[
|
||||||
|
"minikube",
|
||||||
|
"minikube-user",
|
||||||
|
"kubelet",
|
||||||
|
"kops",
|
||||||
|
"admin",
|
||||||
|
"kube",
|
||||||
|
"kube-proxy",
|
||||||
|
"kube-apiserver-healthcheck",
|
||||||
"kubernetes-admin",
|
"kubernetes-admin",
|
||||||
vertical_pod_autoscaler_users,
|
vertical_pod_autoscaler_users,
|
||||||
]
|
]
|
||||||
@@ -115,6 +123,7 @@
|
|||||||
- macro: health_endpoint
|
- macro: health_endpoint
|
||||||
condition: ka.uri=/healthz
|
condition: ka.uri=/healthz
|
||||||
|
|
||||||
|
# requires FALCO_ENGINE_VERSION 5
|
||||||
- rule: Create Disallowed Pod
|
- rule: Create Disallowed Pod
|
||||||
desc: >
|
desc: >
|
||||||
Detect an attempt to start a pod with a container image outside of a list of allowed images.
|
Detect an attempt to start a pod with a container image outside of a list of allowed images.
|
||||||
@@ -124,6 +133,7 @@
|
|||||||
source: k8s_audit
|
source: k8s_audit
|
||||||
tags: [k8s]
|
tags: [k8s]
|
||||||
|
|
||||||
|
# requires FALCO_ENGINE_VERSION 5
|
||||||
- rule: Create Privileged Pod
|
- rule: Create Privileged Pod
|
||||||
desc: >
|
desc: >
|
||||||
Detect an attempt to start a pod with a privileged container
|
Detect an attempt to start a pod with a privileged container
|
||||||
@@ -137,6 +147,7 @@
|
|||||||
condition: >
|
condition: >
|
||||||
(ka.req.pod.volumes.hostpath intersects (/proc, /var/run/docker.sock, /, /etc, /root, /var/run/crio/crio.sock, /home/admin, /var/lib/kubelet, /var/lib/kubelet/pki, /etc/kubernetes, /etc/kubernetes/manifests))
|
(ka.req.pod.volumes.hostpath intersects (/proc, /var/run/docker.sock, /, /etc, /root, /var/run/crio/crio.sock, /home/admin, /var/lib/kubelet, /var/lib/kubelet/pki, /etc/kubernetes, /etc/kubernetes/manifests))
|
||||||
|
|
||||||
|
# requires FALCO_ENGINE_VERSION 5
|
||||||
- rule: Create Sensitive Mount Pod
|
- rule: Create Sensitive Mount Pod
|
||||||
desc: >
|
desc: >
|
||||||
Detect an attempt to start a pod with a volume from a sensitive host directory (i.e. /proc).
|
Detect an attempt to start a pod with a volume from a sensitive host directory (i.e. /proc).
|
||||||
@@ -148,6 +159,7 @@
|
|||||||
tags: [k8s]
|
tags: [k8s]
|
||||||
|
|
||||||
# Corresponds to K8s CIS Benchmark 1.7.4
|
# Corresponds to K8s CIS Benchmark 1.7.4
|
||||||
|
# requires FALCO_ENGINE_VERSION 5
|
||||||
- rule: Create HostNetwork Pod
|
- rule: Create HostNetwork Pod
|
||||||
desc: Detect an attempt to start a pod using the host network.
|
desc: Detect an attempt to start a pod using the host network.
|
||||||
condition: kevt and pod and kcreate and ka.req.pod.host_network intersects (true) and not ka.req.pod.containers.image.repository in (falco_hostnetwork_images)
|
condition: kevt and pod and kcreate and ka.req.pod.host_network intersects (true) and not ka.req.pod.containers.image.repository in (falco_hostnetwork_images)
|
||||||
@@ -236,6 +248,7 @@
|
|||||||
condition: (ka.req.pod.containers.image.repository in (user_trusted_image_list))
|
condition: (ka.req.pod.containers.image.repository in (user_trusted_image_list))
|
||||||
|
|
||||||
# Detect any new pod created in the kube-system namespace
|
# Detect any new pod created in the kube-system namespace
|
||||||
|
# requires FALCO_ENGINE_VERSION 5
|
||||||
- rule: Pod Created in Kube Namespace
|
- rule: Pod Created in Kube Namespace
|
||||||
desc: Detect any attempt to create a pod in the kube-system or kube-public namespaces
|
desc: Detect any attempt to create a pod in the kube-system or kube-public namespaces
|
||||||
condition: kevt and pod and kcreate and ka.target.namespace in (kube-system, kube-public) and not trusted_pod
|
condition: kevt and pod and kcreate and ka.target.namespace in (kube-system, kube-public) and not trusted_pod
|
||||||
@@ -280,6 +293,7 @@
|
|||||||
source: k8s_audit
|
source: k8s_audit
|
||||||
tags: [k8s]
|
tags: [k8s]
|
||||||
|
|
||||||
|
# requires FALCO_ENGINE_VERSION 5
|
||||||
- rule: ClusterRole With Wildcard Created
|
- rule: ClusterRole With Wildcard Created
|
||||||
desc: Detect any attempt to create a Role/ClusterRole with wildcard resources or verbs
|
desc: Detect any attempt to create a Role/ClusterRole with wildcard resources or verbs
|
||||||
condition: kevt and (role or clusterrole) and kcreate and (ka.req.role.rules.resources intersects ("*") or ka.req.role.rules.verbs intersects ("*"))
|
condition: kevt and (role or clusterrole) and kcreate and (ka.req.role.rules.resources intersects ("*") or ka.req.role.rules.verbs intersects ("*"))
|
||||||
@@ -292,6 +306,7 @@
|
|||||||
condition: >
|
condition: >
|
||||||
(ka.req.role.rules.verbs intersects (create, update, patch, delete, deletecollection))
|
(ka.req.role.rules.verbs intersects (create, update, patch, delete, deletecollection))
|
||||||
|
|
||||||
|
# requires FALCO_ENGINE_VERSION 5
|
||||||
- rule: ClusterRole With Write Privileges Created
|
- rule: ClusterRole With Write Privileges Created
|
||||||
desc: Detect any attempt to create a Role/ClusterRole that can perform write-related actions
|
desc: Detect any attempt to create a Role/ClusterRole that can perform write-related actions
|
||||||
condition: kevt and (role or clusterrole) and kcreate and writable_verbs
|
condition: kevt and (role or clusterrole) and kcreate and writable_verbs
|
||||||
@@ -300,6 +315,7 @@
|
|||||||
source: k8s_audit
|
source: k8s_audit
|
||||||
tags: [k8s]
|
tags: [k8s]
|
||||||
|
|
||||||
|
# requires FALCO_ENGINE_VERSION 5
|
||||||
- rule: ClusterRole With Pod Exec Created
|
- rule: ClusterRole With Pod Exec Created
|
||||||
desc: Detect any attempt to create a Role/ClusterRole that can exec to pods
|
desc: Detect any attempt to create a Role/ClusterRole that can exec to pods
|
||||||
condition: kevt and (role or clusterrole) and kcreate and ka.req.role.rules.resources intersects ("pods/exec")
|
condition: kevt and (role or clusterrole) and kcreate and ka.req.role.rules.resources intersects ("pods/exec")
|
||||||
@@ -463,14 +479,20 @@
|
|||||||
source: k8s_audit
|
source: k8s_audit
|
||||||
tags: [k8s]
|
tags: [k8s]
|
||||||
|
|
||||||
|
|
||||||
# This macro disables following rule, change to k8s_audit_never_true to enable it
|
# This macro disables following rule, change to k8s_audit_never_true to enable it
|
||||||
- macro: allowed_full_admin_users
|
- macro: allowed_full_admin_users
|
||||||
condition: (k8s_audit_always_true)
|
condition: (k8s_audit_always_true)
|
||||||
|
|
||||||
# This list includes some of the default user names for an administrator in several K8s installations
|
# This list includes some of the default user names for an administrator in several K8s installations
|
||||||
- list: full_admin_k8s_users
|
- list: full_admin_k8s_users
|
||||||
items: ["admin", "kubernetes-admin", "kubernetes-admin@kubernetes", "kubernetes-admin@cluster.local", "minikube-user"]
|
items:
|
||||||
|
[
|
||||||
|
"admin",
|
||||||
|
"kubernetes-admin",
|
||||||
|
"kubernetes-admin@kubernetes",
|
||||||
|
"kubernetes-admin@cluster.local",
|
||||||
|
"minikube-user",
|
||||||
|
]
|
||||||
|
|
||||||
# This rules detect an operation triggered by an user name that is
|
# This rules detect an operation triggered by an user name that is
|
||||||
# included in the list of those that are default administrators upon
|
# included in the list of those that are default administrators upon
|
||||||
@@ -572,4 +594,3 @@
|
|||||||
priority: WARNING
|
priority: WARNING
|
||||||
source: k8s_audit
|
source: k8s_audit
|
||||||
tags: [k8s]
|
tags: [k8s]
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,2 @@
|
|||||||
- macro: allowed_k8s_containers
|
- macro: allowed_k8s_containers
|
||||||
condition: (ka.req.pod.containers.image.repository in (nginx))
|
condition: (ka.req.pod.containers.image.repository in (nginx))
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
-- Copyright (C) 2019 The Falco Authors.
|
-- Copyright (C) 2020 The Falco Authors.
|
||||||
--
|
--
|
||||||
-- Licensed under the Apache License, Version 2.0 (the "License");
|
-- Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
-- you may not use this file except in compliance with the License.
|
-- you may not use this file except in compliance with the License.
|
||||||
@@ -11,7 +11,6 @@
|
|||||||
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
-- See the License for the specific language governing permissions and
|
-- See the License for the specific language governing permissions and
|
||||||
-- limitations under the License.
|
-- limitations under the License.
|
||||||
|
|
||||||
local parser = require("parser")
|
local parser = require("parser")
|
||||||
local compiler = {}
|
local compiler = {}
|
||||||
|
|
||||||
@@ -19,14 +18,14 @@ compiler.trim = parser.trim
|
|||||||
|
|
||||||
function map(f, arr)
|
function map(f, arr)
|
||||||
local res = {}
|
local res = {}
|
||||||
for i,v in ipairs(arr) do
|
for i, v in ipairs(arr) do
|
||||||
res[i] = f(v)
|
res[i] = f(v)
|
||||||
end
|
end
|
||||||
return res
|
return res
|
||||||
end
|
end
|
||||||
|
|
||||||
function foldr(f, acc, arr)
|
function foldr(f, acc, arr)
|
||||||
for i,v in pairs(arr) do
|
for i, v in pairs(arr) do
|
||||||
acc = f(acc, v)
|
acc = f(acc, v)
|
||||||
end
|
end
|
||||||
return acc
|
return acc
|
||||||
@@ -47,9 +46,13 @@ end
|
|||||||
--]]
|
--]]
|
||||||
|
|
||||||
function copy_ast_obj(obj)
|
function copy_ast_obj(obj)
|
||||||
if type(obj) ~= 'table' then return obj end
|
if type(obj) ~= 'table' then
|
||||||
|
return obj
|
||||||
|
end
|
||||||
local res = {}
|
local res = {}
|
||||||
for k, v in pairs(obj) do res[copy_ast_obj(k)] = copy_ast_obj(v) end
|
for k, v in pairs(obj) do
|
||||||
|
res[copy_ast_obj(k)] = copy_ast_obj(v)
|
||||||
|
end
|
||||||
return res
|
return res
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -60,7 +63,7 @@ function expand_macros(ast, defs, changed)
|
|||||||
elseif ast.type == "Filter" then
|
elseif ast.type == "Filter" then
|
||||||
if (ast.value.type == "Macro") then
|
if (ast.value.type == "Macro") then
|
||||||
if (defs[ast.value.value] == nil) then
|
if (defs[ast.value.value] == nil) then
|
||||||
return false, "Undefined macro '".. ast.value.value .. "' used in filter."
|
return false, "Undefined macro '" .. ast.value.value .. "' used in filter."
|
||||||
end
|
end
|
||||||
defs[ast.value.value].used = true
|
defs[ast.value.value].used = true
|
||||||
ast.value = copy_ast_obj(defs[ast.value.value].ast)
|
ast.value = copy_ast_obj(defs[ast.value.value].ast)
|
||||||
@@ -73,7 +76,7 @@ function expand_macros(ast, defs, changed)
|
|||||||
|
|
||||||
if (ast.left.type == "Macro") then
|
if (ast.left.type == "Macro") then
|
||||||
if (defs[ast.left.value] == nil) then
|
if (defs[ast.left.value] == nil) then
|
||||||
return false, "Undefined macro '".. ast.left.value .. "' used in filter."
|
return false, "Undefined macro '" .. ast.left.value .. "' used in filter."
|
||||||
end
|
end
|
||||||
defs[ast.left.value].used = true
|
defs[ast.left.value].used = true
|
||||||
ast.left = copy_ast_obj(defs[ast.left.value].ast)
|
ast.left = copy_ast_obj(defs[ast.left.value].ast)
|
||||||
@@ -82,7 +85,7 @@ function expand_macros(ast, defs, changed)
|
|||||||
|
|
||||||
if (ast.right.type == "Macro") then
|
if (ast.right.type == "Macro") then
|
||||||
if (defs[ast.right.value] == nil) then
|
if (defs[ast.right.value] == nil) then
|
||||||
return false, "Undefined macro ".. ast.right.value .. " used in filter."
|
return false, "Undefined macro " .. ast.right.value .. " used in filter."
|
||||||
end
|
end
|
||||||
defs[ast.right.value].used = true
|
defs[ast.right.value].used = true
|
||||||
ast.right = copy_ast_obj(defs[ast.right.value].ast)
|
ast.right = copy_ast_obj(defs[ast.right.value].ast)
|
||||||
@@ -102,7 +105,7 @@ function expand_macros(ast, defs, changed)
|
|||||||
elseif ast.type == "UnaryBoolOp" then
|
elseif ast.type == "UnaryBoolOp" then
|
||||||
if (ast.argument.type == "Macro") then
|
if (ast.argument.type == "Macro") then
|
||||||
if (defs[ast.argument.value] == nil) then
|
if (defs[ast.argument.value] == nil) then
|
||||||
return false, "Undefined macro ".. ast.argument.value .. " used in filter."
|
return false, "Undefined macro " .. ast.argument.value .. " used in filter."
|
||||||
end
|
end
|
||||||
defs[ast.argument.value].used = true
|
defs[ast.argument.value].used = true
|
||||||
ast.argument = copy_ast_obj(defs[ast.argument.value].ast)
|
ast.argument = copy_ast_obj(defs[ast.argument.value].ast)
|
||||||
@@ -127,8 +130,12 @@ function get_macros(ast, set)
|
|||||||
local left = get_macros(ast.left, {})
|
local left = get_macros(ast.left, {})
|
||||||
local right = get_macros(ast.right, {})
|
local right = get_macros(ast.right, {})
|
||||||
|
|
||||||
for m, _ in pairs(left) do set[m] = true end
|
for m, _ in pairs(left) do
|
||||||
for m, _ in pairs(right) do set[m] = true end
|
set[m] = true
|
||||||
|
end
|
||||||
|
for m, _ in pairs(right) do
|
||||||
|
set[m] = true
|
||||||
|
end
|
||||||
|
|
||||||
return set
|
return set
|
||||||
end
|
end
|
||||||
@@ -148,7 +155,9 @@ function get_filters(ast)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
parser.traverse_ast(ast.filter.value, {FieldName=1} , cb)
|
parser.traverse_ast(ast.filter.value, {
|
||||||
|
FieldName = 1
|
||||||
|
}, cb)
|
||||||
|
|
||||||
return filters
|
return filters
|
||||||
end
|
end
|
||||||
@@ -165,26 +174,27 @@ function compiler.expand_lists_in(source, list_defs)
|
|||||||
local epos = bpos + string.len(name)
|
local epos = bpos + string.len(name)
|
||||||
|
|
||||||
-- The characters surrounding the name must be delimiters of beginning/end of string
|
-- The characters surrounding the name must be delimiters of beginning/end of string
|
||||||
if (bpos == 1 or string.match(string.sub(source, bpos-1, bpos-1), "[%s(),=]")) and (epos > string.len(source) or string.match(string.sub(source, epos, epos), "[%s(),=]")) then
|
if (bpos == 1 or string.match(string.sub(source, bpos - 1, bpos - 1), "[%s(),=]")) and
|
||||||
|
(epos > string.len(source) or string.match(string.sub(source, epos, epos), "[%s(),=]")) then
|
||||||
new_source = ""
|
new_source = ""
|
||||||
|
|
||||||
if bpos > 1 then
|
if bpos > 1 then
|
||||||
new_source = new_source..string.sub(source, 1, bpos-1)
|
new_source = new_source .. string.sub(source, 1, bpos - 1)
|
||||||
end
|
end
|
||||||
|
|
||||||
sub = table.concat(def.items, ", ")
|
sub = table.concat(def.items, ", ")
|
||||||
|
|
||||||
new_source = new_source..sub
|
new_source = new_source .. sub
|
||||||
|
|
||||||
if epos <= string.len(source) then
|
if epos <= string.len(source) then
|
||||||
new_source = new_source..string.sub(source, epos, string.len(source))
|
new_source = new_source .. string.sub(source, epos, string.len(source))
|
||||||
end
|
end
|
||||||
|
|
||||||
source = new_source
|
source = new_source
|
||||||
bpos = bpos + (string.len(sub)-string.len(name))
|
bpos = bpos + (string.len(sub) - string.len(name))
|
||||||
end
|
end
|
||||||
|
|
||||||
bpos = string.find(source, name, bpos+1, true)
|
bpos = string.find(source, name, bpos + 1, true)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -198,7 +208,7 @@ function compiler.compile_macro(line, macro_defs, list_defs)
|
|||||||
local ast, error_msg = parser.parse_filter(line)
|
local ast, error_msg = parser.parse_filter(line)
|
||||||
|
|
||||||
if (error_msg) then
|
if (error_msg) then
|
||||||
msg = "Compilation error when compiling \""..line.."\": ".. error_msg
|
msg = "Compilation error when compiling \"" .. line .. "\": " .. error_msg
|
||||||
return false, msg
|
return false, msg
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -212,13 +222,13 @@ function compiler.compile_macro(line, macro_defs, list_defs)
|
|||||||
repeat
|
repeat
|
||||||
status, expanded = expand_macros(ast_copy, macro_defs, false)
|
status, expanded = expand_macros(ast_copy, macro_defs, false)
|
||||||
if status == false then
|
if status == false then
|
||||||
msg = "Compilation error when compiling \""..line.."\": ".. expanded
|
msg = "Compilation error when compiling \"" .. line .. "\": " .. expanded
|
||||||
return false, msg
|
return false, msg
|
||||||
end
|
end
|
||||||
until expanded == false
|
until expanded == false
|
||||||
|
|
||||||
else
|
else
|
||||||
return false, "Unexpected top-level AST type: "..ast.type
|
return false, "Unexpected top-level AST type: " .. ast.type
|
||||||
end
|
end
|
||||||
|
|
||||||
return true, ast
|
return true, ast
|
||||||
@@ -234,7 +244,7 @@ function compiler.compile_filter(name, source, macro_defs, list_defs)
|
|||||||
local ast, error_msg = parser.parse_filter(source)
|
local ast, error_msg = parser.parse_filter(source)
|
||||||
|
|
||||||
if (error_msg) then
|
if (error_msg) then
|
||||||
msg = "Compilation error when compiling \""..source.."\": "..error_msg
|
msg = "Compilation error when compiling \"" .. source .. "\": " .. error_msg
|
||||||
return false, msg
|
return false, msg
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -248,7 +258,7 @@ function compiler.compile_filter(name, source, macro_defs, list_defs)
|
|||||||
until expanded == false
|
until expanded == false
|
||||||
|
|
||||||
else
|
else
|
||||||
return false, "Unexpected top-level AST type: "..ast.type
|
return false, "Unexpected top-level AST type: " .. ast.type
|
||||||
end
|
end
|
||||||
|
|
||||||
filters = get_filters(ast)
|
filters = get_filters(ast)
|
||||||
@@ -256,5 +266,4 @@ function compiler.compile_filter(name, source, macro_defs, list_defs)
|
|||||||
return true, ast, filters
|
return true, ast, filters
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
return compiler
|
return compiler
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
-- Copyright (C) 2019 The Falco Authors.
|
-- Copyright (C) 2020 The Falco Authors.
|
||||||
--
|
--
|
||||||
-- Licensed under the Apache License, Version 2.0 (the "License");
|
-- Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
-- you may not use this file except in compliance with the License.
|
-- you may not use this file except in compliance with the License.
|
||||||
@@ -12,7 +12,6 @@
|
|||||||
-- See the License for the specific language governing permissions and
|
-- See the License for the specific language governing permissions and
|
||||||
-- limitations under the License.
|
-- limitations under the License.
|
||||||
--
|
--
|
||||||
|
|
||||||
--[[
|
--[[
|
||||||
Falco grammar and parser.
|
Falco grammar and parser.
|
||||||
|
|
||||||
@@ -108,11 +107,14 @@ end
|
|||||||
|
|
||||||
local function list(pat, sep)
|
local function list(pat, sep)
|
||||||
return Ct(pat ^ -1 * (sep * pat ^ 0) ^ 0) / function(elements)
|
return Ct(pat ^ -1 * (sep * pat ^ 0) ^ 0) / function(elements)
|
||||||
return {type = "List", elements = elements}
|
return {
|
||||||
|
type = "List",
|
||||||
|
elements = elements
|
||||||
|
}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--http://lua-users.org/wiki/StringTrim
|
-- http://lua-users.org/wiki/StringTrim
|
||||||
function trim(s)
|
function trim(s)
|
||||||
if (type(s) ~= "string") then
|
if (type(s) ~= "string") then
|
||||||
return s
|
return s
|
||||||
@@ -128,23 +130,39 @@ local function terminal(tag)
|
|||||||
if tag ~= "String" then
|
if tag ~= "String" then
|
||||||
val = trim(tok)
|
val = trim(tok)
|
||||||
end
|
end
|
||||||
return {type = tag, value = val}
|
return {
|
||||||
|
type = tag,
|
||||||
|
value = val
|
||||||
|
}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function unaryboolop(op, e)
|
local function unaryboolop(op, e)
|
||||||
return {type = "UnaryBoolOp", operator = op, argument = e}
|
return {
|
||||||
|
type = "UnaryBoolOp",
|
||||||
|
operator = op,
|
||||||
|
argument = e
|
||||||
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
local function unaryrelop(e, op)
|
local function unaryrelop(e, op)
|
||||||
return {type = "UnaryRelOp", operator = op, argument = e}
|
return {
|
||||||
|
type = "UnaryRelOp",
|
||||||
|
operator = op,
|
||||||
|
argument = e
|
||||||
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
local function binaryop(e1, op, e2)
|
local function binaryop(e1, op, e2)
|
||||||
if not op then
|
if not op then
|
||||||
return e1
|
return e1
|
||||||
else
|
else
|
||||||
return {type = "BinaryBoolOp", operator = op, left = e1, right = e2}
|
return {
|
||||||
|
type = "BinaryBoolOp",
|
||||||
|
operator = op,
|
||||||
|
left = e1,
|
||||||
|
right = e2
|
||||||
|
}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -154,18 +172,29 @@ end
|
|||||||
|
|
||||||
local function rel(left, sep, right)
|
local function rel(left, sep, right)
|
||||||
return left * sep * right / function(e1, op, e2)
|
return left * sep * right / function(e1, op, e2)
|
||||||
return {type = "BinaryRelOp", operator = op, left = e1, right = e2}
|
return {
|
||||||
|
type = "BinaryRelOp",
|
||||||
|
operator = op,
|
||||||
|
left = e1,
|
||||||
|
right = e2
|
||||||
|
}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- grammar
|
-- grammar
|
||||||
|
|
||||||
local function filter(e)
|
local function filter(e)
|
||||||
return {type = "Filter", value = e}
|
return {
|
||||||
|
type = "Filter",
|
||||||
|
value = e
|
||||||
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
local function rule(filter)
|
local function rule(filter)
|
||||||
return {type = "Rule", filter = filter}
|
return {
|
||||||
|
type = "Rule",
|
||||||
|
filter = filter
|
||||||
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
local G = {
|
local G = {
|
||||||
@@ -181,8 +210,7 @@ local G = {
|
|||||||
ExistsExpression = terminal "FieldName" * V "ExistsOp" / unaryrelop + V "MacroExpression",
|
ExistsExpression = terminal "FieldName" * V "ExistsOp" / unaryrelop + V "MacroExpression",
|
||||||
MacroExpression = terminal "Macro" + V "RelationalExpression",
|
MacroExpression = terminal "Macro" + V "RelationalExpression",
|
||||||
RelationalExpression = rel(terminal "FieldName", V "RelOp", V "Value") +
|
RelationalExpression = rel(terminal "FieldName", V "RelOp", V "Value") +
|
||||||
rel(terminal "FieldName", V "SetOp", V "InList") +
|
rel(terminal "FieldName", V "SetOp", V "InList") + V "PrimaryExp",
|
||||||
V "PrimaryExp",
|
|
||||||
PrimaryExp = symb("(") * V "Filter" * symb(")"),
|
PrimaryExp = symb("(") * V "Filter" * symb(")"),
|
||||||
FuncArgs = symb("(") * list(V "Value", symb(",")) * symb(")"),
|
FuncArgs = symb("(") * list(V "Value", symb(",")) * symb(")"),
|
||||||
-- Terminals
|
-- Terminals
|
||||||
@@ -207,20 +235,15 @@ local G = {
|
|||||||
Number = C(V "Hex" + V "Float" + V "Int") / function(n)
|
Number = C(V "Hex" + V "Float" + V "Int") / function(n)
|
||||||
return tonumber(n)
|
return tonumber(n)
|
||||||
end,
|
end,
|
||||||
String = (P '"' * C(((P "\\" * P(1)) + (P(1) - P '"')) ^ 0) * P '"' +
|
String = (P '"' * C(((P "\\" * P(1)) + (P(1) - P '"')) ^ 0) * P '"' + P "'" *
|
||||||
P "'" * C(((P "\\" * P(1)) + (P(1) - P "'")) ^ 0) * P "'"),
|
C(((P "\\" * P(1)) + (P(1) - P "'")) ^ 0) * P "'"),
|
||||||
BareString = C((P(1) - S " (),=") ^ 1),
|
BareString = C((P(1) - S " (),=") ^ 1),
|
||||||
OrOp = kw("or") / "or",
|
OrOp = kw("or") / "or",
|
||||||
AndOp = kw("and") / "and",
|
AndOp = kw("and") / "and",
|
||||||
Colon = kw(":"),
|
Colon = kw(":"),
|
||||||
RelOp = symb("=") / "=" + symb("==") / "==" + symb("!=") / "!=" + symb("<=") / "<=" + symb(">=") / ">=" +
|
RelOp = symb("=") / "=" + symb("==") / "==" + symb("!=") / "!=" + symb("<=") / "<=" + symb(">=") / ">=" + symb("<") /
|
||||||
symb("<") / "<" +
|
"<" + symb(">") / ">" + symb("contains") / "contains" + symb("icontains") / "icontains" + symb("glob") / "glob" +
|
||||||
symb(">") / ">" +
|
symb("startswith") / "startswith" + symb("endswith") / "endswith",
|
||||||
symb("contains") / "contains" +
|
|
||||||
symb("icontains") / "icontains" +
|
|
||||||
symb("glob") / "glob" +
|
|
||||||
symb("startswith") / "startswith" +
|
|
||||||
symb("endswith") / "endswith",
|
|
||||||
SetOp = kw("in") / "in" + kw("intersects") / "intersects" + kw("pmatch") / "pmatch",
|
SetOp = kw("in") / "in" + kw("intersects") / "intersects" + kw("pmatch") / "pmatch",
|
||||||
UnaryBoolOp = kw("not") / "not",
|
UnaryBoolOp = kw("not") / "not",
|
||||||
ExistsOp = kw("exists") / "exists",
|
ExistsOp = kw("exists") / "exists",
|
||||||
@@ -232,7 +255,9 @@ local G = {
|
|||||||
Parses a single filter and returns the AST.
|
Parses a single filter and returns the AST.
|
||||||
--]]
|
--]]
|
||||||
function parser.parse_filter(subject)
|
function parser.parse_filter(subject)
|
||||||
local errorinfo = {subject = subject}
|
local errorinfo = {
|
||||||
|
subject = subject
|
||||||
|
}
|
||||||
lpeg.setmaxstack(1000)
|
lpeg.setmaxstack(1000)
|
||||||
local ast, error_msg = lpeg.match(G, subject, nil, errorinfo)
|
local ast, error_msg = lpeg.match(G, subject, nil, errorinfo)
|
||||||
return ast, error_msg
|
return ast, error_msg
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
-- Copyright (C) 2019 The Falco Authors.
|
-- Copyright (C) 2020 The Falco Authors.
|
||||||
--
|
--
|
||||||
-- Licensed under the Apache License, Version 2.0 (the "License");
|
-- Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
-- you may not use this file except in compliance with the License.
|
-- you may not use this file except in compliance with the License.
|
||||||
@@ -12,18 +12,15 @@
|
|||||||
-- See the License for the specific language governing permissions and
|
-- See the License for the specific language governing permissions and
|
||||||
-- limitations under the License.
|
-- limitations under the License.
|
||||||
--
|
--
|
||||||
|
|
||||||
--[[
|
--[[
|
||||||
Compile and install falco rules.
|
Compile and install falco rules.
|
||||||
|
|
||||||
This module exports functions that are called from falco c++-side to compile and install a set of rules.
|
This module exports functions that are called from falco c++-side to compile and install a set of rules.
|
||||||
|
|
||||||
--]]
|
--]]
|
||||||
|
|
||||||
local sinsp_rule_utils = require "sinsp_rule_utils"
|
local sinsp_rule_utils = require "sinsp_rule_utils"
|
||||||
local compiler = require "compiler"
|
local compiler = require "compiler"
|
||||||
local yaml = require"lyaml"
|
local yaml = require "lyaml"
|
||||||
|
|
||||||
|
|
||||||
--[[
|
--[[
|
||||||
Traverse AST, adding the passed-in 'index' to each node that contains a relational expression
|
Traverse AST, adding the passed-in 'index' to each node that contains a relational expression
|
||||||
@@ -45,25 +42,46 @@ local function mark_relational_nodes(ast, index)
|
|||||||
ast.index = index
|
ast.index = index
|
||||||
|
|
||||||
else
|
else
|
||||||
error ("Unexpected type in mark_relational_nodes: "..t)
|
error("Unexpected type in mark_relational_nodes: " .. t)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function map(f, arr)
|
function map(f, arr)
|
||||||
local res = {}
|
local res = {}
|
||||||
for i,v in ipairs(arr) do
|
for i, v in ipairs(arr) do
|
||||||
res[i] = f(v)
|
res[i] = f(v)
|
||||||
end
|
end
|
||||||
return res
|
return res
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
-- Permissive for case and for common abbreviations.
|
-- Permissive for case and for common abbreviations.
|
||||||
priorities = {
|
priorities = {
|
||||||
Emergency=0, Alert=1, Critical=2, Error=3, Warning=4, Notice=5, Informational=5, Debug=7,
|
Emergency = 0,
|
||||||
emergency=0, alert=1, critical=2, error=3, warning=4, notice=5, informational=5, debug=7,
|
Alert = 1,
|
||||||
EMERGENCY=0, ALERT=1, CRITICAL=2, ERROR=3, WARNING=4, NOTICE=5, INFORMATIONAL=5, DEBUG=7,
|
Critical = 2,
|
||||||
INFO=5, info=5
|
Error = 3,
|
||||||
|
Warning = 4,
|
||||||
|
Notice = 5,
|
||||||
|
Informational = 5,
|
||||||
|
Debug = 7,
|
||||||
|
emergency = 0,
|
||||||
|
alert = 1,
|
||||||
|
critical = 2,
|
||||||
|
error = 3,
|
||||||
|
warning = 4,
|
||||||
|
notice = 5,
|
||||||
|
informational = 5,
|
||||||
|
debug = 7,
|
||||||
|
EMERGENCY = 0,
|
||||||
|
ALERT = 1,
|
||||||
|
CRITICAL = 2,
|
||||||
|
ERROR = 3,
|
||||||
|
WARNING = 4,
|
||||||
|
NOTICE = 5,
|
||||||
|
INFORMATIONAL = 5,
|
||||||
|
DEBUG = 7,
|
||||||
|
INFO = 5,
|
||||||
|
info = 5
|
||||||
}
|
}
|
||||||
|
|
||||||
--[[
|
--[[
|
||||||
@@ -77,7 +95,7 @@ local function install_filter(node, filter_api_lib, lua_parser, parent_bool_op)
|
|||||||
-- "nesting" (the runtime equivalent of placing parens in syntax) is
|
-- "nesting" (the runtime equivalent of placing parens in syntax) is
|
||||||
-- never necessary when we have identical successive operators. so we
|
-- never necessary when we have identical successive operators. so we
|
||||||
-- avoid it as a runtime performance optimization.
|
-- avoid it as a runtime performance optimization.
|
||||||
if (not(node.operator == parent_bool_op)) then
|
if (not (node.operator == parent_bool_op)) then
|
||||||
filter_api_lib.nest(lua_parser) -- io.write("(")
|
filter_api_lib.nest(lua_parser) -- io.write("(")
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -90,16 +108,16 @@ local function install_filter(node, filter_api_lib, lua_parser, parent_bool_op)
|
|||||||
end
|
end
|
||||||
|
|
||||||
elseif t == "UnaryBoolOp" then
|
elseif t == "UnaryBoolOp" then
|
||||||
filter_api_lib.nest(lua_parser) --io.write("(")
|
filter_api_lib.nest(lua_parser) -- io.write("(")
|
||||||
filter_api_lib.bool_op(lua_parser, node.operator) -- io.write(" "..node.operator.." ")
|
filter_api_lib.bool_op(lua_parser, node.operator) -- io.write(" "..node.operator.." ")
|
||||||
install_filter(node.argument, filter_api_lib, lua_parser)
|
install_filter(node.argument, filter_api_lib, lua_parser)
|
||||||
filter_api_lib.unnest(lua_parser) -- io.write(")")
|
filter_api_lib.unnest(lua_parser) -- io.write(")")
|
||||||
|
|
||||||
elseif t == "BinaryRelOp" then
|
elseif t == "BinaryRelOp" then
|
||||||
if (node.operator == "in" or
|
if (node.operator == "in" or node.operator == "intersects" or node.operator == "pmatch") then
|
||||||
node.operator == "intersects" or
|
elements = map(function(el)
|
||||||
node.operator == "pmatch") then
|
return el.value
|
||||||
elements = map(function (el) return el.value end, node.right.elements)
|
end, node.right.elements)
|
||||||
filter_api_lib.rel_expr(lua_parser, node.left.value, node.operator, elements, node.index)
|
filter_api_lib.rel_expr(lua_parser, node.left.value, node.operator, elements, node.index)
|
||||||
else
|
else
|
||||||
filter_api_lib.rel_expr(lua_parser, node.left.value, node.operator, node.right.value, node.index)
|
filter_api_lib.rel_expr(lua_parser, node.left.value, node.operator, node.right.value, node.index)
|
||||||
@@ -108,21 +126,21 @@ local function install_filter(node, filter_api_lib, lua_parser, parent_bool_op)
|
|||||||
|
|
||||||
elseif t == "UnaryRelOp" then
|
elseif t == "UnaryRelOp" then
|
||||||
filter_api_lib.rel_expr(lua_parser, node.argument.value, node.operator, node.index)
|
filter_api_lib.rel_expr(lua_parser, node.argument.value, node.operator, node.index)
|
||||||
--io.write(node.argument.value.." "..node.operator)
|
-- io.write(node.argument.value.." "..node.operator)
|
||||||
|
|
||||||
else
|
else
|
||||||
error ("Unexpected type in install_filter: "..t)
|
error("Unexpected type in install_filter: " .. t)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function set_output(output_format, state)
|
function set_output(output_format, state)
|
||||||
|
|
||||||
if(output_ast.type == "OutputFormat") then
|
if (output_ast.type == "OutputFormat") then
|
||||||
|
|
||||||
local format
|
local format
|
||||||
|
|
||||||
else
|
else
|
||||||
error ("Unexpected type in set_output: ".. output_ast.type)
|
error("Unexpected type in set_output: " .. output_ast.type)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -130,9 +148,20 @@ end
|
|||||||
-- object. The by_name index is used for things like describing rules,
|
-- object. The by_name index is used for things like describing rules,
|
||||||
-- and the by_idx index is used to map the relational node index back
|
-- and the by_idx index is used to map the relational node index back
|
||||||
-- to a rule.
|
-- to a rule.
|
||||||
local state = {macros={}, lists={}, filter_ast=nil, rules_by_name={},
|
local state = {
|
||||||
skipped_rules_by_name={}, macros_by_name={}, lists_by_name={},
|
macros = {},
|
||||||
n_rules=0, rules_by_idx={}, ordered_rule_names={}, ordered_macro_names={}, ordered_list_names={}}
|
lists = {},
|
||||||
|
filter_ast = nil,
|
||||||
|
rules_by_name = {},
|
||||||
|
skipped_rules_by_name = {},
|
||||||
|
macros_by_name = {},
|
||||||
|
lists_by_name = {},
|
||||||
|
n_rules = 0,
|
||||||
|
rules_by_idx = {},
|
||||||
|
ordered_rule_names = {},
|
||||||
|
ordered_macro_names = {},
|
||||||
|
ordered_list_names = {}
|
||||||
|
}
|
||||||
|
|
||||||
local function reset_rules(rules_mgr)
|
local function reset_rules(rules_mgr)
|
||||||
falco_rules.clear_filters(rules_mgr)
|
falco_rules.clear_filters(rules_mgr)
|
||||||
@@ -144,40 +173,38 @@ end
|
|||||||
|
|
||||||
-- From http://lua-users.org/wiki/TableUtils
|
-- From http://lua-users.org/wiki/TableUtils
|
||||||
--
|
--
|
||||||
function table.val_to_str ( v )
|
function table.val_to_str(v)
|
||||||
if "string" == type( v ) then
|
if "string" == type(v) then
|
||||||
v = string.gsub( v, "\n", "\\n" )
|
v = string.gsub(v, "\n", "\\n")
|
||||||
if string.match( string.gsub(v,"[^'\"]",""), '^"+$' ) then
|
if string.match(string.gsub(v, "[^'\"]", ""), '^"+$') then
|
||||||
return "'" .. v .. "'"
|
return "'" .. v .. "'"
|
||||||
end
|
end
|
||||||
return '"' .. string.gsub(v,'"', '\\"' ) .. '"'
|
return '"' .. string.gsub(v, '"', '\\"') .. '"'
|
||||||
else
|
else
|
||||||
return "table" == type( v ) and table.tostring( v ) or
|
return "table" == type(v) and table.tostring(v) or tostring(v)
|
||||||
tostring( v )
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function table.key_to_str ( k )
|
function table.key_to_str(k)
|
||||||
if "string" == type( k ) and string.match( k, "^[_%a][_%a%d]*$" ) then
|
if "string" == type(k) and string.match(k, "^[_%a][_%a%d]*$") then
|
||||||
return k
|
return k
|
||||||
else
|
else
|
||||||
return "[" .. table.val_to_str( k ) .. "]"
|
return "[" .. table.val_to_str(k) .. "]"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function table.tostring( tbl )
|
function table.tostring(tbl)
|
||||||
local result, done = {}, {}
|
local result, done = {}, {}
|
||||||
for k, v in ipairs( tbl ) do
|
for k, v in ipairs(tbl) do
|
||||||
table.insert( result, table.val_to_str( v ) )
|
table.insert(result, table.val_to_str(v))
|
||||||
done[ k ] = true
|
done[k] = true
|
||||||
end
|
end
|
||||||
for k, v in pairs( tbl ) do
|
for k, v in pairs(tbl) do
|
||||||
if not done[ k ] then
|
if not done[k] then
|
||||||
table.insert( result,
|
table.insert(result, table.key_to_str(k) .. "=" .. table.val_to_str(v))
|
||||||
table.key_to_str( k ) .. "=" .. table.val_to_str( v ) )
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return "{" .. table.concat( result, "," ) .. "}"
|
return "{" .. table.concat(result, ",") .. "}"
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Split rules_content by lines and also remember the line numbers for
|
-- Split rules_content by lines and also remember the line numbers for
|
||||||
@@ -193,34 +220,34 @@ function split_lines(rules_content)
|
|||||||
pos = string.find(rules_content, "\n", 1, true)
|
pos = string.find(rules_content, "\n", 1, true)
|
||||||
|
|
||||||
while pos ~= nil do
|
while pos ~= nil do
|
||||||
line = string.sub(rules_content, last_pos, pos-1)
|
line = string.sub(rules_content, last_pos, pos - 1)
|
||||||
if line ~= "" then
|
if line ~= "" then
|
||||||
lines[#lines+1] = line
|
lines[#lines + 1] = line
|
||||||
if string.len(line) >= 3 and string.sub(line, 1, 3) == "---" then
|
if string.len(line) >= 3 and string.sub(line, 1, 3) == "---" then
|
||||||
-- Document marker, skip
|
-- Document marker, skip
|
||||||
elseif string.sub(line, 1, 1) == '-' then
|
elseif string.sub(line, 1, 1) == '-' then
|
||||||
indices[#indices+1] = idx
|
indices[#indices + 1] = idx
|
||||||
end
|
end
|
||||||
|
|
||||||
idx = idx + 1
|
idx = idx + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
last_pos = pos+1
|
last_pos = pos + 1
|
||||||
pos = string.find(rules_content, "\n", pos+1, true)
|
pos = string.find(rules_content, "\n", pos + 1, true)
|
||||||
end
|
end
|
||||||
|
|
||||||
if last_pos < string.len(rules_content) then
|
if last_pos < string.len(rules_content) then
|
||||||
line = string.sub(rules_content, last_pos)
|
line = string.sub(rules_content, last_pos)
|
||||||
lines[#lines+1] = line
|
lines[#lines + 1] = line
|
||||||
if string.sub(line, 1, 1) == '-' then
|
if string.sub(line, 1, 1) == '-' then
|
||||||
indices[#indices+1] = idx
|
indices[#indices + 1] = idx
|
||||||
end
|
end
|
||||||
|
|
||||||
idx = idx + 1
|
idx = idx + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Add a final index for last line in document
|
-- Add a final index for last line in document
|
||||||
indices[#indices+1] = idx
|
indices[#indices + 1] = idx
|
||||||
|
|
||||||
return lines, indices
|
return lines, indices
|
||||||
end
|
end
|
||||||
@@ -230,7 +257,7 @@ function get_orig_yaml_obj(rules_lines, row)
|
|||||||
|
|
||||||
idx = row
|
idx = row
|
||||||
while (idx <= #rules_lines) do
|
while (idx <= #rules_lines) do
|
||||||
ret = ret..rules_lines[idx].."\n"
|
ret = ret .. rules_lines[idx] .. "\n"
|
||||||
idx = idx + 1
|
idx = idx + 1
|
||||||
|
|
||||||
if idx > #rules_lines or rules_lines[idx] == "" or string.sub(rules_lines[idx], 1, 1) == '-' then
|
if idx > #rules_lines or rules_lines[idx] == "" or string.sub(rules_lines[idx], 1, 1) == '-' then
|
||||||
@@ -246,7 +273,7 @@ function get_lines(rules_lines, row, num_lines)
|
|||||||
|
|
||||||
idx = row
|
idx = row
|
||||||
while (idx < (row + num_lines) and idx <= #rules_lines) do
|
while (idx < (row + num_lines) and idx <= #rules_lines) do
|
||||||
ret = ret..rules_lines[idx].."\n"
|
ret = ret .. rules_lines[idx] .. "\n"
|
||||||
idx = idx + 1
|
idx = idx + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -254,13 +281,13 @@ function get_lines(rules_lines, row, num_lines)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function build_error(rules_lines, row, num_lines, err)
|
function build_error(rules_lines, row, num_lines, err)
|
||||||
local ret = err.."\n---\n"..get_lines(rules_lines, row, num_lines).."---"
|
local ret = err .. "\n---\n" .. get_lines(rules_lines, row, num_lines) .. "---"
|
||||||
|
|
||||||
return ret
|
return ret
|
||||||
end
|
end
|
||||||
|
|
||||||
function build_error_with_context(ctx, err)
|
function build_error_with_context(ctx, err)
|
||||||
local ret = err.."\n---\n"..ctx.."---"
|
local ret = err .. "\n---\n" .. ctx .. "---"
|
||||||
return ret
|
return ret
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -270,16 +297,16 @@ function load_rules_doc(rules_mgr, doc, load_state)
|
|||||||
-- populating the set of rules, macros, and lists. We're not
|
-- populating the set of rules, macros, and lists. We're not
|
||||||
-- expanding/compiling anything yet. All that will happen in a
|
-- expanding/compiling anything yet. All that will happen in a
|
||||||
-- second pass
|
-- second pass
|
||||||
for i,v in ipairs(doc) do
|
for i, v in ipairs(doc) do
|
||||||
|
|
||||||
load_state.cur_item_idx = load_state.cur_item_idx + 1
|
load_state.cur_item_idx = load_state.cur_item_idx + 1
|
||||||
|
|
||||||
-- Save back the original object as it appeared in the file. Will be used to provide context.
|
-- Save back the original object as it appeared in the file. Will be used to provide context.
|
||||||
local context = get_orig_yaml_obj(load_state.lines,
|
local context = get_orig_yaml_obj(load_state.lines, load_state.indices[load_state.cur_item_idx])
|
||||||
load_state.indices[load_state.cur_item_idx])
|
|
||||||
|
|
||||||
if (not (type(v) == "table")) then
|
if (not (type(v) == "table")) then
|
||||||
return false, build_error_with_context(context, "Unexpected element of type " ..type(v)..". Each element should be a yaml associative array.")
|
return false, build_error_with_context(context, "Unexpected element of type " .. type(v) ..
|
||||||
|
". Each element should be a yaml associative array.")
|
||||||
end
|
end
|
||||||
|
|
||||||
v['context'] = context
|
v['context'] = context
|
||||||
@@ -287,11 +314,14 @@ function load_rules_doc(rules_mgr, doc, load_state)
|
|||||||
if (v['required_engine_version']) then
|
if (v['required_engine_version']) then
|
||||||
load_state.required_engine_version = v['required_engine_version']
|
load_state.required_engine_version = v['required_engine_version']
|
||||||
if type(load_state.required_engine_version) ~= "number" then
|
if type(load_state.required_engine_version) ~= "number" then
|
||||||
return false, build_error_with_context(v['context'], "Value of required_engine_version must be a number")
|
return false,
|
||||||
|
build_error_with_context(v['context'], "Value of required_engine_version must be a number")
|
||||||
end
|
end
|
||||||
|
|
||||||
if falco_rules.engine_version(rules_mgr) < v['required_engine_version'] then
|
if falco_rules.engine_version(rules_mgr) < v['required_engine_version'] then
|
||||||
return false, build_error_with_context(v['context'], "Rules require engine version "..v['required_engine_version']..", but engine version is "..falco_rules.engine_version(rules_mgr))
|
return false, build_error_with_context(v['context'],
|
||||||
|
"Rules require engine version " .. v['required_engine_version'] .. ", but engine version is " ..
|
||||||
|
falco_rules.engine_version(rules_mgr))
|
||||||
end
|
end
|
||||||
|
|
||||||
elseif (v['macro']) then
|
elseif (v['macro']) then
|
||||||
@@ -305,12 +335,12 @@ function load_rules_doc(rules_mgr, doc, load_state)
|
|||||||
end
|
end
|
||||||
|
|
||||||
if state.macros_by_name[v['macro']] == nil then
|
if state.macros_by_name[v['macro']] == nil then
|
||||||
state.ordered_macro_names[#state.ordered_macro_names+1] = v['macro']
|
state.ordered_macro_names[#state.ordered_macro_names + 1] = v['macro']
|
||||||
end
|
end
|
||||||
|
|
||||||
for j, field in ipairs({'condition'}) do
|
for j, field in ipairs({'condition'}) do
|
||||||
if (v[field] == nil) then
|
if (v[field] == nil) then
|
||||||
return false, build_error_with_context(v['context'], "Macro must have property "..field)
|
return false, build_error_with_context(v['context'], "Macro must have property " .. field)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -323,13 +353,16 @@ function load_rules_doc(rules_mgr, doc, load_state)
|
|||||||
|
|
||||||
if append then
|
if append then
|
||||||
if state.macros_by_name[v['macro']] == nil then
|
if state.macros_by_name[v['macro']] == nil then
|
||||||
return false, build_error_with_context(v['context'], "Macro " ..v['macro'].. " has 'append' key but no macro by that name already exists")
|
return false, build_error_with_context(v['context'], "Macro " .. v['macro'] ..
|
||||||
|
" has 'append' key but no macro by that name already exists")
|
||||||
end
|
end
|
||||||
|
|
||||||
state.macros_by_name[v['macro']]['condition'] = state.macros_by_name[v['macro']]['condition'] .. " " .. v['condition']
|
state.macros_by_name[v['macro']]['condition'] =
|
||||||
|
state.macros_by_name[v['macro']]['condition'] .. " " .. v['condition']
|
||||||
|
|
||||||
-- Add the current object to the context of the base macro
|
-- Add the current object to the context of the base macro
|
||||||
state.macros_by_name[v['macro']]['context'] = state.macros_by_name[v['macro']]['context'].."\n"..v['context']
|
state.macros_by_name[v['macro']]['context'] =
|
||||||
|
state.macros_by_name[v['macro']]['context'] .. "\n" .. v['context']
|
||||||
|
|
||||||
else
|
else
|
||||||
state.macros_by_name[v['macro']] = v
|
state.macros_by_name[v['macro']] = v
|
||||||
@@ -342,12 +375,12 @@ function load_rules_doc(rules_mgr, doc, load_state)
|
|||||||
end
|
end
|
||||||
|
|
||||||
if state.lists_by_name[v['list']] == nil then
|
if state.lists_by_name[v['list']] == nil then
|
||||||
state.ordered_list_names[#state.ordered_list_names+1] = v['list']
|
state.ordered_list_names[#state.ordered_list_names + 1] = v['list']
|
||||||
end
|
end
|
||||||
|
|
||||||
for j, field in ipairs({'items'}) do
|
for j, field in ipairs({'items'}) do
|
||||||
if (v[field] == nil) then
|
if (v[field] == nil) then
|
||||||
return false, build_error_with_context(v['context'], "List must have property "..field)
|
return false, build_error_with_context(v['context'], "List must have property " .. field)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -360,7 +393,8 @@ function load_rules_doc(rules_mgr, doc, load_state)
|
|||||||
|
|
||||||
if append then
|
if append then
|
||||||
if state.lists_by_name[v['list']] == nil then
|
if state.lists_by_name[v['list']] == nil then
|
||||||
return false, build_error_with_context(v['context'], "List " ..v['list'].. " has 'append' key but no list by that name already exists")
|
return false, build_error_with_context(v['context'], "List " .. v['list'] ..
|
||||||
|
" has 'append' key but no list by that name already exists")
|
||||||
end
|
end
|
||||||
|
|
||||||
for j, elem in ipairs(v['items']) do
|
for j, elem in ipairs(v['items']) do
|
||||||
@@ -398,26 +432,29 @@ function load_rules_doc(rules_mgr, doc, load_state)
|
|||||||
-- For append rules, all you need is the condition
|
-- For append rules, all you need is the condition
|
||||||
for j, field in ipairs({'condition'}) do
|
for j, field in ipairs({'condition'}) do
|
||||||
if (v[field] == nil) then
|
if (v[field] == nil) then
|
||||||
return false, build_error_with_context(v['context'], "Rule must have property "..field)
|
return false, build_error_with_context(v['context'], "Rule must have property " .. field)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if state.rules_by_name[v['rule']] == nil then
|
if state.rules_by_name[v['rule']] == nil then
|
||||||
if state.skipped_rules_by_name[v['rule']] == nil then
|
if state.skipped_rules_by_name[v['rule']] == nil then
|
||||||
return false, build_error_with_context(v['context'], "Rule " ..v['rule'].. " has 'append' key but no rule by that name already exists")
|
return false, build_error_with_context(v['context'], "Rule " .. v['rule'] ..
|
||||||
|
" has 'append' key but no rule by that name already exists")
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
state.rules_by_name[v['rule']]['condition'] = state.rules_by_name[v['rule']]['condition'] .. " " .. v['condition']
|
state.rules_by_name[v['rule']]['condition'] =
|
||||||
|
state.rules_by_name[v['rule']]['condition'] .. " " .. v['condition']
|
||||||
|
|
||||||
-- Add the current object to the context of the base rule
|
-- Add the current object to the context of the base rule
|
||||||
state.rules_by_name[v['rule']]['context'] = state.rules_by_name[v['rule']]['context'].."\n"..v['context']
|
state.rules_by_name[v['rule']]['context'] =
|
||||||
|
state.rules_by_name[v['rule']]['context'] .. "\n" .. v['context']
|
||||||
end
|
end
|
||||||
|
|
||||||
else
|
else
|
||||||
|
|
||||||
for j, field in ipairs({'condition', 'output', 'desc', 'priority'}) do
|
for j, field in ipairs({'condition', 'output', 'desc', 'priority'}) do
|
||||||
if (v[field] == nil) then
|
if (v[field] == nil) then
|
||||||
return false, build_error_with_context(v['context'], "Rule must have property "..field)
|
return false, build_error_with_context(v['context'], "Rule must have property " .. field)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -425,7 +462,7 @@ function load_rules_doc(rules_mgr, doc, load_state)
|
|||||||
v['priority_num'] = priorities[v['priority']]
|
v['priority_num'] = priorities[v['priority']]
|
||||||
|
|
||||||
if v['priority_num'] == nil then
|
if v['priority_num'] == nil then
|
||||||
error("Invalid priority level: "..v['priority'])
|
error("Invalid priority level: " .. v['priority'])
|
||||||
end
|
end
|
||||||
|
|
||||||
if v['priority_num'] <= load_state.min_priority then
|
if v['priority_num'] <= load_state.min_priority then
|
||||||
@@ -433,7 +470,7 @@ function load_rules_doc(rules_mgr, doc, load_state)
|
|||||||
-- loaded in the order in which they first appeared,
|
-- loaded in the order in which they first appeared,
|
||||||
-- potentially across multiple files.
|
-- potentially across multiple files.
|
||||||
if state.rules_by_name[v['rule']] == nil then
|
if state.rules_by_name[v['rule']] == nil then
|
||||||
state.ordered_rule_names[#state.ordered_rule_names+1] = v['rule']
|
state.ordered_rule_names[#state.ordered_rule_names + 1] = v['rule']
|
||||||
end
|
end
|
||||||
|
|
||||||
-- The output field might be a folded-style, which adds a
|
-- The output field might be a folded-style, which adds a
|
||||||
@@ -449,28 +486,29 @@ function load_rules_doc(rules_mgr, doc, load_state)
|
|||||||
-- Remove the context from the table, so the table is exactly what was parsed
|
-- Remove the context from the table, so the table is exactly what was parsed
|
||||||
local context = v['context']
|
local context = v['context']
|
||||||
v['context'] = nil
|
v['context'] = nil
|
||||||
return false, build_error_with_context(context, "Unknown rule object: "..table.tostring(v))
|
return false, build_error_with_context(context, "Unknown rule object: " .. table.tostring(v))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return true, ""
|
return true, ""
|
||||||
end
|
end
|
||||||
|
|
||||||
function load_rules(sinsp_lua_parser,
|
function load_rules(sinsp_lua_parser, json_lua_parser, rules_content, rules_mgr, verbose, all_events, extra,
|
||||||
json_lua_parser,
|
replace_container_info, min_priority)
|
||||||
rules_content,
|
|
||||||
rules_mgr,
|
|
||||||
verbose,
|
|
||||||
all_events,
|
|
||||||
extra,
|
|
||||||
replace_container_info,
|
|
||||||
min_priority)
|
|
||||||
|
|
||||||
local load_state = {lines={}, indices={}, cur_item_idx=0, min_priority=min_priority, required_engine_version=0}
|
local load_state = {
|
||||||
|
lines = {},
|
||||||
|
indices = {},
|
||||||
|
cur_item_idx = 0,
|
||||||
|
min_priority = min_priority,
|
||||||
|
required_engine_version = 0
|
||||||
|
}
|
||||||
|
|
||||||
load_state.lines, load_state.indices = split_lines(rules_content)
|
load_state.lines, load_state.indices = split_lines(rules_content)
|
||||||
|
|
||||||
local status, docs = pcall(yaml.load, rules_content, { all = true })
|
local status, docs = pcall(yaml.load, rules_content, {
|
||||||
|
all = true
|
||||||
|
})
|
||||||
|
|
||||||
if status == false then
|
if status == false then
|
||||||
local pat = "^([%d]+):([%d]+): "
|
local pat = "^([%d]+):([%d]+): "
|
||||||
@@ -538,15 +576,18 @@ function load_rules(sinsp_lua_parser,
|
|||||||
-- the items and expand any references to the items in the list
|
-- the items and expand any references to the items in the list
|
||||||
for i, item in ipairs(v['items']) do
|
for i, item in ipairs(v['items']) do
|
||||||
if (state.lists[item] == nil) then
|
if (state.lists[item] == nil) then
|
||||||
items[#items+1] = item
|
items[#items + 1] = item
|
||||||
else
|
else
|
||||||
for i, exp_item in ipairs(state.lists[item].items) do
|
for i, exp_item in ipairs(state.lists[item].items) do
|
||||||
items[#items+1] = exp_item
|
items[#items + 1] = exp_item
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
state.lists[v['list']] = {["items"] = items, ["used"] = false}
|
state.lists[v['list']] = {
|
||||||
|
["items"] = items,
|
||||||
|
["used"] = false
|
||||||
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
for _, name in ipairs(state.ordered_macro_names) do
|
for _, name in ipairs(state.ordered_macro_names) do
|
||||||
@@ -565,7 +606,10 @@ function load_rules(sinsp_lua_parser,
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
state.macros[v['macro']] = {["ast"] = ast.filter.value, ["used"] = false}
|
state.macros[v['macro']] = {
|
||||||
|
["ast"] = ast.filter.value,
|
||||||
|
["used"] = false
|
||||||
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
for _, name in ipairs(state.ordered_rule_names) do
|
for _, name in ipairs(state.ordered_rule_names) do
|
||||||
@@ -577,8 +621,8 @@ function load_rules(sinsp_lua_parser,
|
|||||||
warn_evttypes = v['warn_evttypes']
|
warn_evttypes = v['warn_evttypes']
|
||||||
end
|
end
|
||||||
|
|
||||||
local status, filter_ast, filters = compiler.compile_filter(v['rule'], v['condition'],
|
local status, filter_ast, filters =
|
||||||
state.macros, state.lists)
|
compiler.compile_filter(v['rule'], v['condition'], state.macros, state.lists)
|
||||||
|
|
||||||
if status == false then
|
if status == false then
|
||||||
return false, build_error_with_context(v['context'], filter_ast)
|
return false, build_error_with_context(v['context'], filter_ast)
|
||||||
@@ -592,7 +636,8 @@ function load_rules(sinsp_lua_parser,
|
|||||||
sinsp_rule_utils.check_for_ignored_syscalls_events(filter_ast, 'rule', v['rule'])
|
sinsp_rule_utils.check_for_ignored_syscalls_events(filter_ast, 'rule', v['rule'])
|
||||||
end
|
end
|
||||||
|
|
||||||
evttypes, syscallnums = sinsp_rule_utils.get_evttypes_syscalls(name, filter_ast, v['condition'], warn_evttypes, verbose)
|
evttypes, syscallnums = sinsp_rule_utils.get_evttypes_syscalls(name, filter_ast, v['condition'],
|
||||||
|
warn_evttypes, verbose)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- If a filter in the rule doesn't exist, either skip the rule
|
-- If a filter in the rule doesn't exist, either skip the rule
|
||||||
@@ -607,7 +652,7 @@ function load_rules(sinsp_lua_parser,
|
|||||||
bracket_idx = string.find(filter, "[", 1, true)
|
bracket_idx = string.find(filter, "[", 1, true)
|
||||||
|
|
||||||
if bracket_idx ~= nil then
|
if bracket_idx ~= nil then
|
||||||
subfilter = string.sub(filter, 1, bracket_idx-1)
|
subfilter = string.sub(filter, 1, bracket_idx - 1)
|
||||||
|
|
||||||
if defined_arg_filters[subfilter] ~= nil then
|
if defined_arg_filters[subfilter] ~= nil then
|
||||||
found = true
|
found = true
|
||||||
@@ -618,14 +663,14 @@ function load_rules(sinsp_lua_parser,
|
|||||||
dot_idx = string.find(filter, ".", 1, true)
|
dot_idx = string.find(filter, ".", 1, true)
|
||||||
|
|
||||||
while dot_idx ~= nil do
|
while dot_idx ~= nil do
|
||||||
subfilter = string.sub(filter, 1, dot_idx-1)
|
subfilter = string.sub(filter, 1, dot_idx - 1)
|
||||||
|
|
||||||
if defined_arg_filters[subfilter] ~= nil then
|
if defined_arg_filters[subfilter] ~= nil then
|
||||||
found = true
|
found = true
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
|
|
||||||
dot_idx = string.find(filter, ".", dot_idx+1, true)
|
dot_idx = string.find(filter, ".", dot_idx + 1, true)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -633,11 +678,11 @@ function load_rules(sinsp_lua_parser,
|
|||||||
if not found then
|
if not found then
|
||||||
if v['skip-if-unknown-filter'] then
|
if v['skip-if-unknown-filter'] then
|
||||||
if verbose then
|
if verbose then
|
||||||
print("Skipping rule \""..v['rule'].."\" that contains unknown filter "..filter)
|
print("Skipping rule \"" .. v['rule'] .. "\" that contains unknown filter " .. filter)
|
||||||
end
|
end
|
||||||
goto next_rule
|
goto next_rule
|
||||||
else
|
else
|
||||||
error("Rule \""..v['rule'].."\" contains unknown filter "..filter)
|
error("Rule \"" .. v['rule'] .. "\" contains unknown filter " .. filter)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -673,7 +718,12 @@ function load_rules(sinsp_lua_parser,
|
|||||||
if (state.filter_ast == nil) then
|
if (state.filter_ast == nil) then
|
||||||
state.filter_ast = filter_ast.filter.value
|
state.filter_ast = filter_ast.filter.value
|
||||||
else
|
else
|
||||||
state.filter_ast = { type = "BinaryBoolOp", operator = "or", left = state.filter_ast, right = filter_ast.filter.value }
|
state.filter_ast = {
|
||||||
|
type = "BinaryBoolOp",
|
||||||
|
operator = "or",
|
||||||
|
left = state.filter_ast,
|
||||||
|
right = filter_ast.filter.value
|
||||||
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Enable/disable the rule
|
-- Enable/disable the rule
|
||||||
@@ -697,9 +747,10 @@ function load_rules(sinsp_lua_parser,
|
|||||||
-- to replace it, in which case we use the generic
|
-- to replace it, in which case we use the generic
|
||||||
-- "%container.name (id=%container.id)"
|
-- "%container.name (id=%container.id)"
|
||||||
if replace_container_info == false then
|
if replace_container_info == false then
|
||||||
v['output'] = string.gsub(v['output'], "%%container.info", "%%container.name (id=%%container.id)")
|
v['output'] = string.gsub(v['output'], "%%container.info",
|
||||||
|
"%%container.name (id=%%container.id)")
|
||||||
if extra ~= "" then
|
if extra ~= "" then
|
||||||
v['output'] = v['output'].." "..extra
|
v['output'] = v['output'] .. " " .. extra
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
safe_extra = string.gsub(extra, "%%", "%%%%")
|
safe_extra = string.gsub(extra, "%%", "%%%%")
|
||||||
@@ -708,7 +759,7 @@ function load_rules(sinsp_lua_parser,
|
|||||||
else
|
else
|
||||||
-- Just add the extra to the end
|
-- Just add the extra to the end
|
||||||
if extra ~= "" then
|
if extra ~= "" then
|
||||||
v['output'] = v['output'].." "..extra
|
v['output'] = v['output'] .. " " .. extra
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -719,7 +770,7 @@ function load_rules(sinsp_lua_parser,
|
|||||||
formatter = formats.formatter(v['source'], v['output'])
|
formatter = formats.formatter(v['source'], v['output'])
|
||||||
formats.free_formatter(v['source'], formatter)
|
formats.free_formatter(v['source'], formatter)
|
||||||
else
|
else
|
||||||
return false, build_error_with_context(v['context'], "Unexpected type in load_rule: "..filter_ast.type)
|
return false, build_error_with_context(v['context'], "Unexpected type in load_rule: " .. filter_ast.type)
|
||||||
end
|
end
|
||||||
|
|
||||||
::next_rule::
|
::next_rule::
|
||||||
@@ -729,13 +780,13 @@ function load_rules(sinsp_lua_parser,
|
|||||||
-- Print info on any dangling lists or macros that were not used anywhere
|
-- Print info on any dangling lists or macros that were not used anywhere
|
||||||
for name, macro in pairs(state.macros) do
|
for name, macro in pairs(state.macros) do
|
||||||
if macro.used == false then
|
if macro.used == false then
|
||||||
print("Warning: macro "..name.." not refered to by any rule/macro")
|
print("Warning: macro " .. name .. " not refered to by any rule/macro")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
for name, list in pairs(state.lists) do
|
for name, list in pairs(state.lists) do
|
||||||
if list.used == false then
|
if list.used == false then
|
||||||
print("Warning: list "..name.." not refered to by any rule/macro/list")
|
print("Warning: list " .. name .. " not refered to by any rule/macro/list")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -752,18 +803,17 @@ local function wrap(str, limit, indent)
|
|||||||
indent = indent or ""
|
indent = indent or ""
|
||||||
limit = limit or 72
|
limit = limit or 72
|
||||||
local here = 1
|
local here = 1
|
||||||
return str:gsub("(%s+)()(%S+)()",
|
return str:gsub("(%s+)()(%S+)()", function(sp, st, word, fi)
|
||||||
function(sp, st, word, fi)
|
if fi - here > limit then
|
||||||
if fi-here > limit then
|
|
||||||
here = st
|
here = st
|
||||||
return "\n"..indent..word
|
return "\n" .. indent .. word
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function describe_single_rule(name)
|
local function describe_single_rule(name)
|
||||||
if (state.rules_by_name[name] == nil) then
|
if (state.rules_by_name[name] == nil) then
|
||||||
error ("No such rule: "..name)
|
error("No such rule: " .. name)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Wrap the description into an multiple lines each of length ~ 60
|
-- Wrap the description into an multiple lines each of length ~ 60
|
||||||
@@ -793,12 +843,16 @@ function describe_rule(name)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local rule_output_counts = {total=0, by_priority={}, by_name={}}
|
local rule_output_counts = {
|
||||||
|
total = 0,
|
||||||
|
by_priority = {},
|
||||||
|
by_name = {}
|
||||||
|
}
|
||||||
|
|
||||||
function on_event(rule_id)
|
function on_event(rule_id)
|
||||||
|
|
||||||
if state.rules_by_idx[rule_id] == nil then
|
if state.rules_by_idx[rule_id] == nil then
|
||||||
error ("rule_loader.on_event(): event with invalid rule_id: ", rule_id)
|
error("rule_loader.on_event(): event with invalid rule_id: ", rule_id)
|
||||||
end
|
end
|
||||||
|
|
||||||
rule_output_counts.total = rule_output_counts.total + 1
|
rule_output_counts.total = rule_output_counts.total + 1
|
||||||
@@ -817,23 +871,21 @@ function on_event(rule_id)
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- Prefix output with '*' so formatting is permissive
|
-- Prefix output with '*' so formatting is permissive
|
||||||
output = "*"..rule.output
|
output = "*" .. rule.output
|
||||||
|
|
||||||
return rule.rule, rule.priority_num, output
|
return rule.rule, rule.priority_num, output
|
||||||
end
|
end
|
||||||
|
|
||||||
function print_stats()
|
function print_stats()
|
||||||
print("Events detected: "..rule_output_counts.total)
|
print("Events detected: " .. rule_output_counts.total)
|
||||||
print("Rule counts by severity:")
|
print("Rule counts by severity:")
|
||||||
for priority, count in pairs(rule_output_counts.by_priority) do
|
for priority, count in pairs(rule_output_counts.by_priority) do
|
||||||
print (" "..priority..": "..count)
|
print(" " .. priority .. ": " .. count)
|
||||||
end
|
end
|
||||||
|
|
||||||
print("Triggered rules by rule name:")
|
print("Triggered rules by rule name:")
|
||||||
for name, count in pairs(rule_output_counts.by_name) do
|
for name, count in pairs(rule_output_counts.by_name) do
|
||||||
print (" "..name..": "..count)
|
print(" " .. name .. ": " .. count)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
-- Copyright (C) 2019 The Falco Authors.
|
-- Copyright (C) 2020 The Falco Authors.
|
||||||
--
|
--
|
||||||
-- Licensed under the Apache License, Version 2.0 (the "License");
|
-- Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
-- you may not use this file except in compliance with the License.
|
-- you may not use this file except in compliance with the License.
|
||||||
@@ -12,7 +12,6 @@
|
|||||||
-- See the License for the specific language governing permissions and
|
-- See the License for the specific language governing permissions and
|
||||||
-- limitations under the License.
|
-- limitations under the License.
|
||||||
--
|
--
|
||||||
|
|
||||||
local parser = require("parser")
|
local parser = require("parser")
|
||||||
local sinsp_rule_utils = {}
|
local sinsp_rule_utils = {}
|
||||||
|
|
||||||
@@ -20,25 +19,21 @@ function sinsp_rule_utils.check_for_ignored_syscalls_events(ast, filter_type, so
|
|||||||
|
|
||||||
function check_syscall(val)
|
function check_syscall(val)
|
||||||
if ignored_syscalls[val] then
|
if ignored_syscalls[val] then
|
||||||
error("Ignored syscall \""..val.."\" in "..filter_type..": "..source)
|
error("Ignored syscall \"" .. val .. "\" in " .. filter_type .. ": " .. source)
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function check_event(val)
|
function check_event(val)
|
||||||
if ignored_events[val] then
|
if ignored_events[val] then
|
||||||
error("Ignored event \""..val.."\" in "..filter_type..": "..source)
|
error("Ignored event \"" .. val .. "\" in " .. filter_type .. ": " .. source)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function cb(node)
|
function cb(node)
|
||||||
if node.left.type == "FieldName" and
|
if node.left.type == "FieldName" and (node.left.value == "evt.type" or node.left.value == "syscall.type") then
|
||||||
(node.left.value == "evt.type" or
|
|
||||||
node.left.value == "syscall.type") then
|
|
||||||
|
|
||||||
if (node.operator == "in" or
|
if (node.operator == "in" or node.operator == "intersects" or node.operator == "pmatch") then
|
||||||
node.operator == "intersects" or
|
|
||||||
node.operator == "pmatch") then
|
|
||||||
for i, v in ipairs(node.right.elements) do
|
for i, v in ipairs(node.right.elements) do
|
||||||
if v.type == "BareString" then
|
if v.type == "BareString" then
|
||||||
if node.left.value == "evt.type" then
|
if node.left.value == "evt.type" then
|
||||||
@@ -60,7 +55,9 @@ function sinsp_rule_utils.check_for_ignored_syscalls_events(ast, filter_type, so
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
parser.traverse_ast(ast, {BinaryRelOp=1}, cb)
|
parser.traverse_ast(ast, {
|
||||||
|
BinaryRelOp = 1
|
||||||
|
}, cb)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Examine the ast and find the event types/syscalls for which the
|
-- Examine the ast and find the event types/syscalls for which the
|
||||||
@@ -96,15 +93,13 @@ function sinsp_rule_utils.get_evttypes_syscalls(name, ast, source, warn_evttypes
|
|||||||
if found_not then
|
if found_not then
|
||||||
found_event_after_not = true
|
found_event_after_not = true
|
||||||
end
|
end
|
||||||
if (node.operator == "in" or
|
if (node.operator == "in" or node.operator == "intersects" or node.operator == "pmatch") then
|
||||||
node.operator == "intersects" or
|
|
||||||
node.operator == "pmatch") then
|
|
||||||
for i, v in ipairs(node.right.elements) do
|
for i, v in ipairs(node.right.elements) do
|
||||||
if v.type == "BareString" then
|
if v.type == "BareString" then
|
||||||
|
|
||||||
-- The event must be a known event
|
-- The event must be a known event
|
||||||
if events[v.value] == nil and syscalls[v.value] == nil then
|
if events[v.value] == nil and syscalls[v.value] == nil then
|
||||||
error("Unknown event/syscall \""..v.value.."\" in filter: "..source)
|
error("Unknown event/syscall \"" .. v.value .. "\" in filter: " .. source)
|
||||||
end
|
end
|
||||||
|
|
||||||
evtnames[v.value] = 1
|
evtnames[v.value] = 1
|
||||||
@@ -126,7 +121,7 @@ function sinsp_rule_utils.get_evttypes_syscalls(name, ast, source, warn_evttypes
|
|||||||
|
|
||||||
-- The event must be a known event
|
-- The event must be a known event
|
||||||
if events[node.right.value] == nil and syscalls[node.right.value] == nil then
|
if events[node.right.value] == nil and syscalls[node.right.value] == nil then
|
||||||
error("Unknown event/syscall \""..node.right.value.."\" in filter: "..source)
|
error("Unknown event/syscall \"" .. node.right.value .. "\" in filter: " .. source)
|
||||||
end
|
end
|
||||||
|
|
||||||
evtnames[node.right.value] = 1
|
evtnames[node.right.value] = 1
|
||||||
@@ -147,14 +142,19 @@ function sinsp_rule_utils.get_evttypes_syscalls(name, ast, source, warn_evttypes
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
parser.traverse_ast(ast.filter.value, {BinaryRelOp=1, UnaryBoolOp=1} , cb)
|
parser.traverse_ast(ast.filter.value, {
|
||||||
|
BinaryRelOp = 1,
|
||||||
|
UnaryBoolOp = 1
|
||||||
|
}, cb)
|
||||||
|
|
||||||
if not found_event then
|
if not found_event then
|
||||||
if warn_evttypes == true then
|
if warn_evttypes == true then
|
||||||
io.stderr:write("Rule "..name..": warning (no-evttype):\n")
|
io.stderr:write("Rule " .. name .. ": warning (no-evttype):\n")
|
||||||
io.stderr:write(source.."\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(
|
||||||
io.stderr:write(" This has a significant performance penalty. Consider adding an evt.type restriction if possible.\n")
|
" 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
|
end
|
||||||
evttypes = {}
|
evttypes = {}
|
||||||
syscallnums = {}
|
syscallnums = {}
|
||||||
@@ -163,11 +163,12 @@ function sinsp_rule_utils.get_evttypes_syscalls(name, ast, source, warn_evttypes
|
|||||||
|
|
||||||
if found_event_after_not then
|
if found_event_after_not then
|
||||||
if warn_evttypes == true then
|
if warn_evttypes == true then
|
||||||
io.stderr:write("Rule "..name..": warning (trailing-evttype):\n")
|
io.stderr:write("Rule " .. name .. ": warning (trailing-evttype):\n")
|
||||||
io.stderr:write(source.."\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(" 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(" 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(
|
||||||
|
" 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(" 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")
|
io.stderr:write(" replacing negative matches with positive matches if possible.\n")
|
||||||
end
|
end
|
||||||
@@ -190,7 +191,7 @@ function sinsp_rule_utils.get_evttypes_syscalls(name, ast, source, warn_evttypes
|
|||||||
table.sort(evtnames_only)
|
table.sort(evtnames_only)
|
||||||
|
|
||||||
if verbose then
|
if verbose then
|
||||||
io.stderr:write("Event types/Syscalls for rule "..name..": "..table.concat(evtnames_only, ",").."\n")
|
io.stderr:write("Event types/Syscalls for rule " .. name .. ": " .. table.concat(evtnames_only, ",") .. "\n")
|
||||||
end
|
end
|
||||||
|
|
||||||
return evttypes, syscallnums
|
return evttypes, syscallnums
|
||||||
|
|||||||
Reference in New Issue
Block a user