-
Notifications
You must be signed in to change notification settings - Fork 333
Optimize IAST Vulnerability Detection #8885
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 24 commits
416dbae
ea49404
61319c9
b22b445
472547c
0921d3d
97d2a10
b0e2a61
b345cc0
46dea01
4dc2e87
96c0003
1ed6931
21458cf
1ff5c57
73d972e
9f25d16
122f235
8313386
57191f2
9f49817
e237033
5a13cfe
534bd7c
b7ebe05
18c73ce
6eec036
67080d7
2f1d08f
7b0bd85
e0cc794
ffe3efc
3b39517
9cde392
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,16 +3,62 @@ | |
| import static datadog.trace.api.iast.IastDetectionMode.UNLIMITED; | ||
|
|
||
| import com.datadog.iast.util.NonBlockingSemaphore; | ||
| import datadog.trace.api.iast.VulnerabilityTypes; | ||
| import java.util.HashMap; | ||
| import java.util.Map; | ||
| import java.util.Set; | ||
| import java.util.concurrent.ConcurrentHashMap; | ||
| import java.util.concurrent.ConcurrentMap; | ||
| import java.util.concurrent.atomic.AtomicIntegerArray; | ||
| import java.util.function.Function; | ||
| import javax.annotation.Nullable; | ||
| import org.jetbrains.annotations.NotNull; | ||
|
|
||
| public class OverheadContext { | ||
|
|
||
| /** Maximum number of distinct endpoints to remember in the global cache. */ | ||
| private static final int GLOBAL_MAP_MAX_SIZE = 4096; | ||
|
|
||
| /** | ||
| * Global concurrent cache mapping each “method + path” key to its historical vulnerabilityCounts | ||
| * map. As soon as size() > GLOBAL_MAP_MAX_SIZE, we clear() the whole map. | ||
| */ | ||
| static final ConcurrentMap<String, AtomicIntegerArray> globalMap = | ||
| new ConcurrentHashMap<String, AtomicIntegerArray>() { | ||
|
|
||
| @Override | ||
| public AtomicIntegerArray computeIfAbsent( | ||
| String key, | ||
| @NotNull Function<? super String, ? extends AtomicIntegerArray> mappingFunction) { | ||
| AtomicIntegerArray prev = super.computeIfAbsent(key, mappingFunction); | ||
| if (this.size() > GLOBAL_MAP_MAX_SIZE) { | ||
| super.clear(); | ||
| } | ||
| return prev; | ||
| } | ||
| }; | ||
|
|
||
| // Snapshot of the globalMap for the current request | ||
| @Nullable final Map<String, int[]> copyMap; | ||
| // Map of vulnerabilities per endpoint for the current request, needs to use AtomicIntegerArray | ||
| // because it's possible to have concurrent updates in the same request | ||
| @Nullable final Map<String, AtomicIntegerArray> requestMap; | ||
|
|
||
| private final NonBlockingSemaphore availableVulnerabilities; | ||
| private final boolean isGlobal; | ||
|
|
||
| public OverheadContext(final int vulnerabilitiesPerRequest) { | ||
| this(vulnerabilitiesPerRequest, false); | ||
| } | ||
|
|
||
| public OverheadContext(final int vulnerabilitiesPerRequest, final boolean isGlobal) { | ||
| availableVulnerabilities = | ||
| vulnerabilitiesPerRequest == UNLIMITED | ||
| ? NonBlockingSemaphore.unlimited() | ||
| : NonBlockingSemaphore.withPermitCount(vulnerabilitiesPerRequest); | ||
| this.isGlobal = isGlobal; | ||
| this.requestMap = isGlobal ? null : new HashMap<>(); | ||
|
smola marked this conversation as resolved.
Outdated
|
||
| this.copyMap = isGlobal ? null : new HashMap<>(); | ||
| } | ||
|
|
||
| public int getAvailableQuota() { | ||
|
|
@@ -26,4 +72,51 @@ public boolean consumeQuota(final int delta) { | |
| public void reset() { | ||
| availableVulnerabilities.reset(); | ||
| } | ||
|
|
||
| public void resetMaps() { | ||
| // If this is a global context, we do not reset the maps | ||
| if (isGlobal || requestMap == null || copyMap == null) { | ||
| return; | ||
| } | ||
| Set<String> endpoints = requestMap.keySet(); | ||
| // If the budget is not consumed, we can reset the maps | ||
| if (getAvailableQuota() > 0) { | ||
| // clean endpoints from globalMap | ||
| endpoints.forEach(globalMap::remove); | ||
|
smola marked this conversation as resolved.
|
||
| // Clear the requestMap and copyMap related to this context | ||
| requestMap.clear(); | ||
| copyMap.clear(); | ||
| return; | ||
| } | ||
| // If the budget is consumed, we need to merge the requestMap into the globalMap | ||
| endpoints.forEach( | ||
| endpoint -> { | ||
| AtomicIntegerArray countMap = requestMap.get(endpoint); | ||
| // should not happen, but just in case | ||
| if (countMap == null) { | ||
| globalMap.remove(endpoint); | ||
|
smola marked this conversation as resolved.
|
||
| return; | ||
| } | ||
| // Iterate over the vulnerabilities and update the globalMap | ||
| int numberOfVulnerabilities = VulnerabilityTypes.STRINGS.length; | ||
| for (int i = 0; i < numberOfVulnerabilities; i++) { | ||
| int counter = countMap.get(i); | ||
| if (counter > 0) { | ||
| AtomicIntegerArray globalCountMap = | ||
| globalMap.computeIfAbsent( | ||
| endpoint, value -> new AtomicIntegerArray(numberOfVulnerabilities)); | ||
|
|
||
| globalCountMap.accumulateAndGet(i, counter, Math::max); | ||
| } | ||
| } | ||
| }); | ||
|
|
||
| // Clear the requestMap and copyMap related to this context | ||
| requestMap.clear(); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are these maps reused? Do we have to clear them? Or should be enough with just
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. They are final, but it's true that they are not going to be reused - as the whole context instance at this point |
||
| copyMap.clear(); | ||
| } | ||
|
|
||
| public boolean isGlobal() { | ||
| return isGlobal; | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.