Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion fuzzer/cmdi_detector/src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *bytes, size_t size)
}

object_store store;
store.insert(std::move(root));
store.insert_and_apply(std::move(root));

ddwaf::timer deadline{2s};
condition_cache cache;
Expand Down
2 changes: 1 addition & 1 deletion fuzzer/lfi_detector/src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *bytes, size_t size)
owned_object::make_string(resource, ddwaf::memory::get_default_resource()));

object_store store;
store.insert(std::move(root));
store.insert_and_apply(std::move(root));

ddwaf::timer deadline{2s};
condition_cache cache;
Expand Down
2 changes: 1 addition & 1 deletion fuzzer/shi_detector_array/src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *bytes, size_t size)
}

object_store store;
store.insert(std::move(root));
store.insert_and_apply(std::move(root));

ddwaf::timer deadline{2s};
condition_cache cache;
Expand Down
2 changes: 1 addition & 1 deletion fuzzer/shi_detector_string/src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *bytes, size_t size)
owned_object::make_string(resource, ddwaf::memory::get_default_resource()));

object_store store;
store.insert(std::move(root));
store.insert_and_apply(std::move(root));

ddwaf::timer deadline{2s};
condition_cache cache;
Expand Down
2 changes: 1 addition & 1 deletion fuzzer/sqli_detector/src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *bytes, size_t size)
owned_object::make_string(resource, ddwaf::memory::get_default_resource()));

object_store store;
store.insert(std::move(root));
store.insert_and_apply(std::move(root));

ddwaf::timer deadline{2s};
condition_cache cache;
Expand Down
2 changes: 1 addition & 1 deletion fuzzer/ssrf_detector/src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *bytes, size_t size)
owned_object::make_string(resource, ddwaf::memory::get_default_resource()));

object_store store;
store.insert(std::move(root));
store.insert_and_apply(std::move(root));

ddwaf::timer deadline{2s};
condition_cache cache;
Expand Down
179 changes: 179 additions & 0 deletions include/ddwaf.h
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,12 @@ ddwaf_context ddwaf_context_init(const ddwaf_handle handle, ddwaf_allocator outp
* format: {tag, value}
* - keep: whether the data contained herein must override any
* transport sampling through the relevant mechanism.
* - evaluated: an unsigned integer indicating the number of input
* batches that were fully evaluated. For this single
* evaluation it is 1 when a non-empty batch was
* evaluated and 0 otherwise (e.g. an empty input or a
* timeout before evaluation completed). See
* ddwaf_context_multieval for the multi-batch case.
* This structure must be freed by the caller using the output
* allocator provided through ddwaf_context_init. The object will
* contain all specified keys when the value returned by
Expand Down Expand Up @@ -349,6 +355,90 @@ ddwaf_context ddwaf_context_init(const ddwaf_handle handle, ddwaf_allocator outp
DDWAF_RET_CODE ddwaf_context_eval(ddwaf_context context, ddwaf_object *data,
ddwaf_allocator alloc, ddwaf_object *result, uint64_t timeout);

/**
* Perform multiple matching operations on the provided data, evaluating each
* batch in sequence and returning a single combined result.
*
* This function operates identically to ddwaf_context_eval, with the
* distinction that data must be an array of maps. Each element is treated as
* a separate input batch and is evaluated in order. Addresses provided in
* earlier batches persist in the store and remain available when evaluating
* later batches, so a rule that requires multiple addresses can be satisfied
* by data spread across different batches within a single call. The result
* reflects all events, actions, and attributes accumulated across the entire
* sequence of batches.
*
* @param context WAF context to be used in this run, this will determine the
* ruleset which will be used and it will also ensure that
* parameters are taken into account across runs. (nonnull)
*
* @param data (nonnull) Array of input batches to evaluate. Each element must
* be a map of {string, <value>} where each key represents the relevant
* address associated to the value, which can be of an arbitrary type. The
* batches are evaluated in order. If the same address appears in multiple
* batches, the later value replaces the earlier one. Passing a non-array
* object will return DDWAF_ERR_INVALID_OBJECT.
* Ownership and lifetime semantics are identical to ddwaf_context_eval:
* the data is stored by the context and the provided allocator is used to
* free it once the context is destroyed.
*
* @param alloc (nullable) Allocator used to free the data provided. If NULL,
* the data will not be freed.
*
* @param result (nullable) Object map containing the following items:
* - events: an array of all events generated across all batches.
* - actions: a map of all actions generated across all batches
* in the format: "{action type: { <parameter map> }, ...}"
* - duration: an unsigned specifying the total runtime of the
* call in nanoseconds.
* - timeout: whether there has been a timeout during the call.
* - attributes: a map containing all derived objects in the
* format: {tag, value}
* - keep: whether the data contained herein must override any
* transport sampling through the relevant mechanism.
* - evaluated: an unsigned integer indicating the number of
* batches that were fully evaluated. In the normal
* case this equals the number of non-empty batches.
* On timeout or error occurring during batch I
* (0-based, counting only non-empty batches), this
* value equals I, which is also the index of the
* batch where the problem occurred. Empty batches are
* skipped and do not count towards this value.
* This structure must be freed by the caller using the output
* allocator provided through ddwaf_context_init. The object will
* contain all specified keys when the value returned by
* ddwaf_context_multieval is either DDWAF_OK or DDWAF_MATCH and
* will be empty otherwise.
* IMPORTANT: This object is not allocated with the allocator
* passed in this call. It uses the allocator given to
* ddwaf_context_init instead.
* @param timeout Maximum time budget in microseconds.
*
* @return Return code of the operation.
* @retval DDWAF_ERR_INVALID_ARGUMENT The context is invalid, the data will not
* be freed.
* @retval DDWAF_ERR_INVALID_OBJECT The data provided didn't match the desired
* structure or contained invalid objects, the
* data will be freed by this function.
* @retval DDWAF_ERR_INTERNAL There was an unexpected error and the operation did
* not succeed. The state of the WAF is undefined if
* this error is produced and the ownership of the
* data is unknown. The result structure will not be
* filled if this error occurs.
*
* Notes on addresses:
* - Within a single batch, addresses provided should be unique.
* If duplicate addresses are provided within the same batch, the latest one
* in the structure will be used for evaluation.
* - Addresses from earlier batches persist in the store and are accessible
* during evaluation of subsequent batches within the same call. If the same
* address appears in a later batch, the later value replaces the earlier one.
* - A rule that requires multiple addresses can be satisfied by data spread
* across different batches within a single call.
**/
DDWAF_RET_CODE ddwaf_context_multieval(ddwaf_context context, ddwaf_object *data,
ddwaf_allocator alloc, ddwaf_object *result, uint64_t timeout);

/**
* Performs the destruction of the context, freeing the data passed to it through
* ddwaf_context_eval using the provided allocator during evaluation.
Expand Down Expand Up @@ -395,6 +485,12 @@ ddwaf_subcontext ddwaf_subcontext_init(ddwaf_context context);
* format: {tag, value}
* - keep: whether the data contained herein must override any
* transport sampling through the relevant mechanism.
* - evaluated: an unsigned integer indicating the number of input
* batches that were fully evaluated. For this single
* evaluation it is 1 when a non-empty batch was
* evaluated and 0 otherwise (e.g. an empty input or a
* timeout before evaluation completed). See
* ddwaf_subcontext_multieval for the multi-batch case.
* This structure must be freed by the caller and will contain all
* specified keys when the value returned by ddwaf_subcontext_eval
* is either DDWAF_OK or DDWAF_MATCH and will be empty otherwise.
Expand Down Expand Up @@ -425,6 +521,89 @@ ddwaf_subcontext ddwaf_subcontext_init(ddwaf_context context);
DDWAF_RET_CODE ddwaf_subcontext_eval(ddwaf_subcontext subcontext, ddwaf_object *data,
ddwaf_allocator alloc, ddwaf_object *result, uint64_t timeout);

/**
* Perform multiple matching operations on the provided data, evaluating each
* batch in sequence and returning a single combined result.
*
* This function operates identically to ddwaf_subcontext_eval, with the
* distinction that data must be an array of maps. Each element is treated as
* a separate input batch and is evaluated in order. Addresses provided in
* earlier batches persist in the store and remain available when evaluating
* later batches, so a rule that requires multiple addresses can be satisfied
* by data spread across different batches within a single call. The result
* reflects all events, actions, and attributes accumulated across the entire
* sequence of batches.
*
* @param subcontext WAF subcontext to be used in this run, this will determine
* the ruleset which will be used and it will also ensure that
* parameters are taken into account across runs. (nonnull)
*
* @param data (nonnull) Array of input batches to evaluate. Each element must
* be a map of {string, <value>} where each key represents the relevant
* address associated to the value, which can be of an arbitrary type. The
* batches are evaluated in order. If the same address appears in multiple
* batches, the later value replaces the earlier one. Passing a non-array
* object will return DDWAF_ERR_INVALID_OBJECT.
* Ownership and lifetime semantics are identical to ddwaf_subcontext_eval:
* the data is stored by the subcontext and the provided allocator is used
* to free it once the subcontext is destroyed.
*
* @param alloc (nullable) Allocator used to free the data provided. If NULL,
* the data will not be freed.
*
* @param result (nullable) Object map containing the following items:
* - events: an array of all events generated across all batches.
* - actions: a map of all actions generated across all batches
* in the format: "{action type: { <parameter map> }, ...}"
* - duration: an unsigned specifying the total runtime of the
* call in nanoseconds.
* - timeout: whether there has been a timeout during the call.
* - attributes: a map containing all derived objects in the
* format: {tag, value}
* - keep: whether the data contained herein must override any
* transport sampling through the relevant mechanism.
* - evaluated: an unsigned integer indicating the number of
* batches that were fully evaluated. In the normal
* case this equals the number of non-empty batches.
* On timeout or error occurring during batch I
* (0-based, counting only non-empty batches), this
* value equals I, which is also the index of the
* batch where the problem occurred. Empty batches are
* skipped and do not count towards this value.
* This structure must be freed by the caller and will contain all
* specified keys when the value returned by
* ddwaf_subcontext_multieval is either DDWAF_OK or DDWAF_MATCH
* and will be empty otherwise.
* IMPORTANT: This object is not allocated with the allocator
* passed in this call. It uses the allocator given to
* ddwaf_context_init instead.
* @param timeout Maximum time budget in microseconds.
*
* @return Return code of the operation.
* @retval DDWAF_ERR_INVALID_ARGUMENT The subcontext is invalid, the data will
* not be freed.
* @retval DDWAF_ERR_INVALID_OBJECT The data provided didn't match the desired
* structure or contained invalid objects, the
* data will be freed by this function.
* @retval DDWAF_ERR_INTERNAL There was an unexpected error and the operation did
* not succeed. The state of the WAF is undefined if
* this error is produced and the ownership of the
* data is unknown. The result structure will not be
* filled if this error occurs.
*
* Notes on addresses:
* - Within a single batch, addresses provided should be unique.
* If duplicate addresses are provided within the same batch, the latest one
* in the structure will be used for evaluation.
* - Addresses from earlier batches persist in the store and are accessible
* during evaluation of subsequent batches within the same call. If the same
* address appears in a later batch, the later value replaces the earlier one.
* - A rule that requires multiple addresses can be satisfied by data spread
* across different batches within a single call.
**/
DDWAF_RET_CODE ddwaf_subcontext_multieval(ddwaf_subcontext subcontext, ddwaf_object *data,
ddwaf_allocator alloc, ddwaf_object *result, uint64_t timeout);

/**
* Performs the destruction of the subcontext, freeing the data passed to it through
* ddwaf_subcontext_eval using the used-defined allocator.
Expand Down
2 changes: 2 additions & 0 deletions libddwaf.def
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@ EXPORTS
ddwaf_known_addresses
ddwaf_context_init
ddwaf_context_eval
ddwaf_context_multieval
ddwaf_context_destroy
ddwaf_subcontext_init
ddwaf_subcontext_eval
ddwaf_subcontext_multieval
ddwaf_subcontext_destroy
ddwaf_object_destroy
ddwaf_get_version
Expand Down
8 changes: 7 additions & 1 deletion schema/result.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@
"type": "integer",
"minimum": 0,
"description": "The total runtime of the evaluation cycle in nanoseconds"
},
"evaluated": {
"type": "integer",
"minimum": 0,
"description": "The number of input batches that were fully evaluated during the cycle; on timeout or error it is the index of the batch where the problem occurred"
}
},
"required": [
Expand All @@ -37,7 +42,8 @@
"actions",
"attributes",
"timeout",
"duration"
"duration",
"evaluated"
],
"additionalProperties": false
}
29 changes: 29 additions & 0 deletions smoketest/smoke.c
Original file line number Diff line number Diff line change
Expand Up @@ -273,5 +273,34 @@ int main() {
puts("result is valid");
ddwaf_object_destroy(&result, alloc);

// Exercise the multieval entrypoint. It takes an array of input batches
// (each a map of addresses); the actual data is irrelevant here, this just
// verifies the symbol is exported and the library loads correctly.
ddwaf_context multi_ctx = ddwaf_context_init(handle, alloc);
if (!multi_ctx) {
puts("multi_ctx is null");
return 1;
}

ddwaf_object multi_data;
ddwaf_object_set_array(&multi_data, 1, alloc);

ddwaf_object *batch = ddwaf_object_insert(&multi_data, alloc);
ddwaf_object_set_map(batch, 1, alloc);
ddwaf_object_set_string(
ddwaf_object_insert_key(batch, STRL("key"), alloc), STRL("Arachni"), alloc);

ddwaf_object multi_result = {0};
ddwaf_context_multieval(multi_ctx, &multi_data, alloc, &multi_result, (uint32_t)-1);

const ddwaf_object *multi_events =
ddwaf_object_find(&multi_result, "events", sizeof("events") - 1);
if (ddwaf_object_get_size(multi_events) == 0) {
puts("multieval result is empty");
return 1;
}
puts("multieval result is valid");
ddwaf_object_destroy(&multi_result, alloc);

return 0;
}
Loading
Loading