Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
26 changes: 26 additions & 0 deletions docs/Docusaurus/docs/modules/engine_sca/engine_container.md
Original file line number Diff line number Diff line change
Expand Up @@ -246,13 +246,38 @@ Defines exclusion rules for repositories and specific vulnerability findings.
"hu": "345345"
}
]
},
"BY_PATTERN_SEARCH": {
".*_Repository_Test": {
"THRESHOLD": {
"VULNERABILITY": {
"Critical": 1,
"High": 4,
"Medium": 10,
"Low": 15
}
},
"PRISMA": [
{
"id": "CVE-2023-6237",
"cve_id": "CVE-2023-6237",
"expired_date": "21092024",
"create_date": "24012023",
"hu": "345345"
}
]
}
}
}
```

#### Exclusion Types
- **All**: Global exclusions applied to all repositories
- **Repository-specific**: Exclusions for specific repositories (e.g., `"Repository_Test"`)
- **BY_PATTERN_SEARCH**: Regex-based exclusions matching multiple pipeline names at once. Each key is a regex evaluated with `re.match(pattern, pipeline_name, re.IGNORECASE)` against the pipeline name; the first matching pattern's block is used
- Supports both **THRESHOLD** overrides and **tool-specific exclusion lists** (e.g., `PRISMA`), the same way an exact repository match does
- Only evaluated when there is no exact repository key match in `Exclusions.json` (exact match always takes precedence over pattern match)
- Note: base-image filtering (`key_image_exception`/`source_images`) only applies to the `"All"` block; it is not applied to repository-specific or `BY_PATTERN_SEARCH` matches
- **Tool-specific exclusions**: Organized by scanning tool:
- `PRISMA`: Exclusions for Prisma Cloud findings
- `TRIVY`: Exclusions for Trivy findings (if needed)
Expand Down Expand Up @@ -348,6 +373,7 @@ devsecops-engine-tools \
3. Set realistic expiration dates and review expired exclusions regularly
4. Use repository-specific exclusions instead of global ones when possible
5. Document business justification for risk acceptance
6. Use `BY_PATTERN_SEARCH` when the same THRESHOLD and/or tool exclusions must apply to a group of pipelines matching a naming convention (e.g., `".*_Repository_Test"`), instead of duplicating the same block across many repository-specific entries

### SBOM Configuration
1. Enable SBOM generation for production branches only to reduce overhead
Expand Down
26 changes: 26 additions & 0 deletions docs/Docusaurus/docs/modules/engine_sca/engine_dependencies.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,13 +114,38 @@ Defines exclusion rules for repositories and specific vulnerability findings.
"hu": "4662904"
}
]
},
"BY_PATTERN_SEARCH": {
".*_Repository_Test": {
"THRESHOLD": {
"VULNERABILITY": {
"Critical": 1,
"High": 4,
"Medium": 10,
"Low": 15
}
},
"XRAY": [
{
"id": "XRAY-522015",
"cve_id": "CVE-2023-35116",
"where": "all",
"create_date": "19022024",
"expired_date": "undefined",
"hu": "4662904"
}
]
}
}
}
```

#### Exclusion Types
- **All**: Global exclusions applied to all repositories
- **Repository-specific**: Exclusions for specific repositories (can be added as needed)
- **BY_PATTERN_SEARCH**: Regex-based exclusions matching multiple pipeline names at once. Each key is a regex evaluated with `re.match(pattern, pipeline_name, re.IGNORECASE)` against the pipeline name; the first matching pattern's block is used
- Supports both **THRESHOLD** overrides and **tool-specific exclusion lists** (e.g., `XRAY`), the same way an exact repository match does
- Only evaluated when there is no exact repository key match in `Exclusions.json` (exact match always takes precedence over pattern match)
- **Tool-specific exclusions**: Organized by scanning tool:
- `XRAY`: Exclusions for JFrog Xray findings
- `DEPENDENCY_CHECK`: Exclusions for OWASP Dependency-Check findings
Expand Down Expand Up @@ -228,6 +253,7 @@ devsecops-engine-tools \
3. Regularly review and clean up expired exclusions
4. Use global exclusions sparingly - prefer repository-specific exclusions
5. Document business justification for permanent exclusions
6. Use `BY_PATTERN_SEARCH` when the same THRESHOLD and/or tool exclusions must apply to a group of pipelines matching a naming convention (e.g., `".*_Repository_Test"`), instead of duplicating the same block across many repository-specific entries

### Error Handling Optimization
1. Customize `STDERR_EXPECTED_WORDS` based on your scanning environment
Expand Down
Comment thread
jcamilomolinar marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,26 @@
"hu": "345345"
}
]
},
"BY_PATTERN_SEARCH": {
".*_Repository_Test": {
"THRESHOLD": {
"VULNERABILITY": {
"Critical": 1,
"High": 4,
"Medium": 10,
"Low": 15
}
},
"PRISMA": [
{
"id": "CVE-2023-6237",
"cve_id": "CVE-2023-6237",
"expired_date": "21092024",
"create_date": "24012023",
"hu": "345345"
}
]
}
}
}
Comment thread
jcamilomolinar marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,27 @@
"hu": "4662904"
}
]
},
"BY_PATTERN_SEARCH": {
".*_Repository_Test": {
"THRESHOLD": {
"VULNERABILITY": {
"Critical": 1,
"High": 4,
"Medium": 10,
"Low": 15
}
},
"XRAY": [
{
"id": "XRAY-522015",
"cve_id": "CVE-2023-35116",
"where": "all",
"create_date": "19022024",
"expired_date": "undefined",
"hu": "4662904"
}
]
}
}
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import re

from devsecops_engine_tools.engine_core.src.domain.model.input_core import InputCore
from devsecops_engine_tools.engine_core.src.domain.model.exclusions import Exclusions
from devsecops_engine_tools.engine_core.src.domain.model.threshold import Threshold
Expand All @@ -12,6 +14,32 @@ def __init__(self, remote_config, exclusions, pipeline_name, tool, stage):
self.tool = tool
self.stage = stage

def _build_exclusions(self, items, base_image_list, filter_by_base_image):
list_exclusions = []
for item in items:
if filter_by_base_image:
key_image_exception = self.remote_config.get("GET_IMAGE_BASE", {}).get("LABEL_KEYS", {}).get("key_image_exception", None)
source_images = item.get(key_image_exception, [])
if source_images and not base_image_list:
continue
if source_images and not any(img in source for img in base_image_list for source in source_images):
continue

list_exclusions.append(
Exclusions(
id=item.get("id", ""),
where=item.get("where", ""),
cve_id=item.get("cve_id", ""),
create_date=item.get("create_date", ""),
expired_date=item.get("expired_date", ""),
severity=item.get("severity", ""),
priority=item.get("priority", ""),
hu=item.get("hu", ""),
reason=item.get("reason", "DevSecOps policy"),
)
)
return list_exclusions

def get_exclusions(self, exclusions_data, pipeline_name, tool, base_image):
list_exclusions = []
base_image_list = base_image[0][0] if base_image else None
Expand All @@ -20,28 +48,20 @@ def get_exclusions(self, exclusions_data, pipeline_name, tool, base_image):
if key not in {"All", pipeline_name} or not value.get(tool):
continue

for item in value[tool]:
if key == "All":
key_image_exception = self.remote_config.get("GET_IMAGE_BASE", {}).get("LABEL_KEYS", {}).get("key_image_exception", None)
source_images = item.get(key_image_exception, [])
if source_images and not base_image_list:
continue
if source_images and not any(img in source for img in base_image_list for source in source_images):
continue

list_exclusions.append(
Exclusions(
id=item.get("id", ""),
where=item.get("where", ""),
cve_id=item.get("cve_id", ""),
create_date=item.get("create_date", ""),
expired_date=item.get("expired_date", ""),
severity=item.get("severity", ""),
priority=item.get("priority", ""),
hu=item.get("hu", ""),
reason=item.get("reason", "DevSecOps policy"),
)
)
list_exclusions.extend(
self._build_exclusions(value[tool], base_image_list, key == "All")
)

if pipeline_name not in exclusions_data:
for pattern, values in exclusions_data.get(
"BY_PATTERN_SEARCH", {}
).items():
if re.match(pattern, pipeline_name, re.IGNORECASE):
if values.get(tool):
list_exclusions.extend(
self._build_exclusions(values[tool], base_image_list, False)
)
break

return list_exclusions

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,3 +122,66 @@ def test_get_exclusions_no_matching_exclusions(mock_tool_remote):
exclusions = set_input_core.get_exclusions(exclusions_data, pipeline_name, "PRISMA",base_image)

assert len(exclusions) == 0


def test_get_exclusions_by_pattern_search(mock_tool_remote):
exclusions_data = {
"All": {
"PRISMA": [
{
"id": "CVE-2023-5363",
"where": "all",
"hu": "",
}
]
},
"BY_PATTERN_SEARCH": {
".*_Repository_Test": {
"THRESHOLD": {"VULNERABILITY": {"Critical": 1}},
"PRISMA": [
{
"id": "CVE-2023-6237",
"cve_id": "CVE-2023-6237",
"where": "all",
}
],
}
},
}
pipeline_name = "my_Repository_Test"
base_image = [["base_image:latest"]]

set_input_core = SetInputCore(
mock_tool_remote, None, pipeline_name, "PRISMA", "release"
)
exclusions = set_input_core.get_exclusions(
exclusions_data, pipeline_name, "PRISMA", base_image
)

assert len(exclusions) == 2
assert any(item.id == "CVE-2023-6237" for item in exclusions)


def test_get_exclusions_direct_match_takes_precedence_over_pattern(mock_tool_remote):
exclusions_data = {
"my_Repository_Test": {
"PRISMA": [{"id": "direct-match", "where": "all"}]
},
"BY_PATTERN_SEARCH": {
".*_Repository_Test": {
"PRISMA": [{"id": "pattern-match", "where": "all"}],
}
},
}
pipeline_name = "my_Repository_Test"
base_image = [["base_image:latest"]]

set_input_core = SetInputCore(
mock_tool_remote, None, pipeline_name, "PRISMA", "release"
)
exclusions = set_input_core.get_exclusions(
exclusions_data, pipeline_name, "PRISMA", base_image
)

assert len(exclusions) == 1
assert exclusions[0].id == "direct-match"
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import re

from devsecops_engine_tools.engine_core.src.domain.model.input_core import InputCore
from devsecops_engine_tools.engine_core.src.domain.model.threshold import Threshold
from devsecops_engine_tools.engine_core.src.domain.model.exclusions import Exclusions
Expand All @@ -11,26 +13,38 @@
self.pipeline_name = pipeline_name
self.tool = tool

def _build_exclusions(self, items):
return [
Exclusions(
id=item.get("id", ""),
where=item.get("where", ""),
cve_id=item.get("cve_id", ""),
create_date=item.get("create_date", ""),
expired_date=item.get("expired_date", ""),
severity=item.get("severity", ""),
priority=item.get("priority", ""),
hu=item.get("hu", ""),
reason=item.get("reason", "DevSecOps policy"),
)
for item in items
]

def get_exclusions(self, exclusions_data, pipeline_name, tool):

Check failure on line 32 in tools/devsecops_engine_tools/engine_sca/engine_dependencies/src/domain/usecases/set_input_core.py

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Refactor this function to reduce its Cognitive Complexity from 17 to the 15 allowed.

See more on https://sonarcloud.io/project/issues?id=bancolombia_devsecops-engine-tools&issues=AZ8jJ0PbRFoArOAa-u4l&open=AZ8jJ0PbRFoArOAa-u4l&pullRequest=671
list_exclusions = []
for key, value in exclusions_data.items():
if (key == "All") or (key == pipeline_name):
if value.get(tool, 0):
exclusions = [
Exclusions(
id=item.get("id", ""),
where=item.get("where", ""),
cve_id=item.get("cve_id", ""),
create_date=item.get("create_date", ""),
expired_date=item.get("expired_date", ""),
severity=item.get("severity", ""),
priority=item.get("priority", ""),
hu=item.get("hu", ""),
reason=item.get("reason", "DevSecOps policy"),
)
for item in value[tool]
]
list_exclusions.extend(exclusions)
list_exclusions.extend(self._build_exclusions(value[tool]))

if pipeline_name not in exclusions_data:
for pattern, values in exclusions_data.get(
"BY_PATTERN_SEARCH", {}
).items():
if re.match(pattern, pipeline_name, re.IGNORECASE):
if values.get(tool, 0):
list_exclusions.extend(self._build_exclusions(values[tool]))
break

return list_exclusions

def set_input_core(self, dependencies_scanned):
Expand Down
Loading
Loading