Skip to content

Instantly share code, notes, and snippets.

@gormaniac
Last active July 18, 2023 22:52
Show Gist options
  • Save gormaniac/67ddddf4df879b171f082383cd0dc780 to your computer and use it in GitHub Desktop.
Save gormaniac/67ddddf4df879b171f082383cd0dc780 to your computer and use it in GitHub Desktop.
Yield results of a storm query that are passed through an arbitrary query filter
// Just the example code, without comments.
$filter="-#test.tag"
$start="{ it:app:yara:rule=$rule | "
$end=" return($node) }"
$instruction=$lib.str.concat($start, $filter, $end)
$rules=$lib.list()
it:app:yara:rule:enabled=true |
$rules.append($node.value()) |
spin |
fini {
for $rule in $rules {
yield $lib.storm.eval($instruction)
}
}
// This example works on it:app:yara:rule nodes. It will need some tweaks to work with other node types.
//
// The general principal of execution is:
// 1. Build a subquery that will be executed for each node that the initial query lifts via an "eval" call
// - This subquery expects to have a variable available to its runtime that provides a method to lift the necessary node
// - In our example, this variable is "$rule" and it points to a GUID for each it:app:yara:rule lifted by our initial query
// 2. Create a list to store all GUIDs (or $node.values) of each node returned by our initial query.
// 3. Execute our initial query to lift all the nodes we care about into the pipeline.
// 4. Pipe these nodes to a Storm expression that adds the return of "$node.value()" for each node in the pipeline to our GUID list from earlier.
// 5. Pipe again to a "spin" command to drop all of the nodes lifted by our initial query.
// 6. Declare a "fini" block that will run once all nodes are through the initial pipeline as described up to this step.
// This block loops over all GUIDs we saved earlier and passes each GUID to the subquery we created in Step 1.
//
// The rules for the subquery are quite strict. It must return only 0 or 1 nodes, it cannot return more. This is a limitation
// of the "eval" call this code relies on. This is why we have to wait until all nodes are lifted and their values are stored
// until we re yield the nodes individually.
// Change this var to the user supplied filter. It can be any arbitrary Storm query that only expects to yield 0-1 nodes.
$filter="-#test.tag"
// This is the first part of your query that is inserted before the filter.
$start="{ it:app:yara:rule=$rule | "
// This is the part of the query after the filter. Y
$end=" return($node) }"
// This is the full instruction with start filter and end joined together. You can dynamically build this any other way as well.
$instruction=$lib.str.concat($start, $filter, $end)
// Stores the GUIDs of all it:app:yara:rules returned by our initial query
$rules=$lib.list()
// Run the initial query
it:app:yara:rule:enabled=true |
// Append the GUID to our list
$rules.append($node.value()) |
// Spin to drop all the nodes lifted by our original query
spin |
//
fini {
for $rule in $rules {
yield $lib.storm.eval($instruction)
}
}
@gormaniac
Copy link
Author

A solution was shared with me that will actually perform the filter of the initial query lift as each node is lifted:

$filter="-#test.tag"
$iden=$lib.null
$instruction=`\{ yield $iden {$filter} return($node) }`
it:app:yara:rule:enabled=true
$iden = $node.iden()
+$lib.storm.eval($instruction)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment