fluidattacks_core_models 14.2.0__tar.gz → 14.4.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- fluidattacks_core_models-14.4.0/.envrc +1 -0
- fluidattacks_core_models-14.4.0/.gitignore +15 -0
- {fluidattacks_core_models-14.2.0 → fluidattacks_core_models-14.4.0}/PKG-INFO +1 -1
- {fluidattacks_core_models-14.2.0 → fluidattacks_core_models-14.4.0}/fluidattacks_core/models/scanners/__init__.py +7 -1
- {fluidattacks_core_models-14.2.0 → fluidattacks_core_models-14.4.0}/fluidattacks_core/models/scanners/cspm_config.py +14 -4
- {fluidattacks_core_models-14.2.0 → fluidattacks_core_models-14.4.0}/fluidattacks_core/models/scanners/loader.py +4 -1
- {fluidattacks_core_models-14.2.0 → fluidattacks_core_models-14.4.0}/fluidattacks_core/models/scanners/sspm_config.py +13 -0
- {fluidattacks_core_models-14.2.0 → fluidattacks_core_models-14.4.0}/pyproject.toml +1 -1
- {fluidattacks_core_models-14.2.0 → fluidattacks_core_models-14.4.0}/tests/test_loader_env.py +74 -2
- fluidattacks_core_models-14.4.0/tests/test_sspm_config.py +38 -0
- {fluidattacks_core_models-14.2.0 → fluidattacks_core_models-14.4.0}/uv.lock +1 -1
- fluidattacks_core_models-14.2.0/.envrc +0 -1
- fluidattacks_core_models-14.2.0/.gitignore +0 -9
- {fluidattacks_core_models-14.2.0 → fluidattacks_core_models-14.4.0}/fluidattacks_core/models/__init__.py +0 -0
- {fluidattacks_core_models-14.2.0 → fluidattacks_core_models-14.4.0}/fluidattacks_core/models/py.typed +0 -0
- {fluidattacks_core_models-14.2.0 → fluidattacks_core_models-14.4.0}/fluidattacks_core/models/scanners/apk_config.py +0 -0
- {fluidattacks_core_models-14.2.0 → fluidattacks_core_models-14.4.0}/fluidattacks_core/models/scanners/common_config.py +0 -0
- {fluidattacks_core_models-14.2.0 → fluidattacks_core_models-14.4.0}/fluidattacks_core/models/scanners/containers_sca_config.py +0 -0
- {fluidattacks_core_models-14.2.0 → fluidattacks_core_models-14.4.0}/fluidattacks_core/models/scanners/dast_config.py +0 -0
- {fluidattacks_core_models-14.2.0 → fluidattacks_core_models-14.4.0}/fluidattacks_core/models/scanners/resolves_config.py +0 -0
- {fluidattacks_core_models-14.2.0 → fluidattacks_core_models-14.4.0}/fluidattacks_core/models/scanners/sast_config.py +0 -0
- {fluidattacks_core_models-14.2.0 → fluidattacks_core_models-14.4.0}/fluidattacks_core/models/scanners/sbom_config.py +0 -0
- {fluidattacks_core_models-14.2.0 → fluidattacks_core_models-14.4.0}/fluidattacks_core/models/scanners/sca_config.py +0 -0
- {fluidattacks_core_models-14.2.0 → fluidattacks_core_models-14.4.0}/fluidattacks_core/models/scanners/scan_config.py +0 -0
- {fluidattacks_core_models-14.2.0 → fluidattacks_core_models-14.4.0}/fluidattacks_core/models/scanners/sniffs_config.py +0 -0
- {fluidattacks_core_models-14.2.0 → fluidattacks_core_models-14.4.0}/fluidattacks_core/models/state_machines/__init__.py +0 -0
- {fluidattacks_core_models-14.2.0 → fluidattacks_core_models-14.4.0}/fluidattacks_core/models/state_machines/component_orchestrator.py +0 -0
- {fluidattacks_core_models-14.2.0 → fluidattacks_core_models-14.4.0}/fluidattacks_core/models/state_machines/root_scanner.py +0 -0
- {fluidattacks_core_models-14.2.0 → fluidattacks_core_models-14.4.0}/fluidattacks_core/models/state_machines/types.py +0 -0
- {fluidattacks_core_models-14.2.0 → fluidattacks_core_models-14.4.0}/import-linter.cfg +0 -0
- {fluidattacks_core_models-14.2.0 → fluidattacks_core_models-14.4.0}/mypy.ini +0 -0
- {fluidattacks_core_models-14.2.0 → fluidattacks_core_models-14.4.0}/ruff.toml +0 -0
- {fluidattacks_core_models-14.2.0 → fluidattacks_core_models-14.4.0}/tests/__init__.py +0 -0
- {fluidattacks_core_models-14.2.0 → fluidattacks_core_models-14.4.0}/tests/test_component_orchestrator.py +0 -0
- {fluidattacks_core_models-14.2.0 → fluidattacks_core_models-14.4.0}/tests/test_model_scanner.py +0 -0
- {fluidattacks_core_models-14.2.0 → fluidattacks_core_models-14.4.0}/tests/test_root_scanner.py +0 -0
- {fluidattacks_core_models-14.2.0 → fluidattacks_core_models-14.4.0}/tests/test_state_machines.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
use flake ../../..#fluidattacks-core-models
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
dist/
|
|
2
|
+
dist-*/
|
|
3
|
+
*.zip
|
|
4
|
+
.tflint.d
|
|
5
|
+
/.cargo-cache
|
|
6
|
+
target/
|
|
7
|
+
.direnv/
|
|
8
|
+
.venv/
|
|
9
|
+
.vscode/settings.json
|
|
10
|
+
.zed/settings.json
|
|
11
|
+
# each package's shell writes its own, beside the package rather than here
|
|
12
|
+
**/.vscode/settings.json
|
|
13
|
+
*.so
|
|
14
|
+
# the publish check builds into its own tree per architecture
|
|
15
|
+
target-*/
|
|
@@ -33,13 +33,19 @@ from fluidattacks_core.models.scanners.sbom_config import (
|
|
|
33
33
|
from fluidattacks_core.models.scanners.sca_config import ScaConfig
|
|
34
34
|
from fluidattacks_core.models.scanners.scan_config import ScanConfig
|
|
35
35
|
from fluidattacks_core.models.scanners.sniffs_config import SniffsConfig
|
|
36
|
-
from fluidattacks_core.models.scanners.sspm_config import
|
|
36
|
+
from fluidattacks_core.models.scanners.sspm_config import (
|
|
37
|
+
AzureDevopsPat,
|
|
38
|
+
GithubApp,
|
|
39
|
+
GithubPat,
|
|
40
|
+
SspmConfig,
|
|
41
|
+
)
|
|
37
42
|
|
|
38
43
|
__all__ = [
|
|
39
44
|
"ApkConfig",
|
|
40
45
|
"ApkEnvironment",
|
|
41
46
|
"AwsCredentials",
|
|
42
47
|
"AwsRole",
|
|
48
|
+
"AzureDevopsPat",
|
|
43
49
|
"ContainerImageConfig",
|
|
44
50
|
"ContainerScaItem",
|
|
45
51
|
"ContainersScaConfig",
|
|
@@ -6,15 +6,20 @@ from fluidattacks_core.models.scanners.loader import LoaderValidationInfo, resol
|
|
|
6
6
|
class AwsCredentials(BaseModel):
|
|
7
7
|
model_config = {"extra": "forbid"}
|
|
8
8
|
access_key_id: str
|
|
9
|
-
secret_access_key: str
|
|
10
|
-
session_token: str | None = None
|
|
9
|
+
secret_access_key: str = Field(repr=False)
|
|
10
|
+
session_token: str | None = Field(default=None, repr=False)
|
|
11
11
|
environment_id: str | None = None
|
|
12
12
|
|
|
13
|
-
@field_validator("access_key_id", "secret_access_key",
|
|
13
|
+
@field_validator("access_key_id", "secret_access_key", mode="before")
|
|
14
14
|
@classmethod
|
|
15
15
|
def _resolve_credentials(cls, v: object, info: LoaderValidationInfo) -> object:
|
|
16
16
|
return resolve_env_var(v, info)
|
|
17
17
|
|
|
18
|
+
@field_validator("session_token", mode="before")
|
|
19
|
+
@classmethod
|
|
20
|
+
def _resolve_optional_credentials(cls, v: object, info: LoaderValidationInfo) -> object:
|
|
21
|
+
return resolve_env_var(v, info, required=False)
|
|
22
|
+
|
|
18
23
|
@model_validator(mode="after")
|
|
19
24
|
def _validate_key_session_token_pairing(self) -> "AwsCredentials":
|
|
20
25
|
is_temporary = self.access_key_id.startswith("ASIA")
|
|
@@ -33,11 +38,16 @@ class AwsRole(BaseModel):
|
|
|
33
38
|
external_id: str | None = Field(default=None, repr=False)
|
|
34
39
|
environment_id: str | None = None
|
|
35
40
|
|
|
36
|
-
@field_validator("role",
|
|
41
|
+
@field_validator("role", mode="before")
|
|
37
42
|
@classmethod
|
|
38
43
|
def _resolve_credentials(cls, v: object, info: LoaderValidationInfo) -> object:
|
|
39
44
|
return resolve_env_var(v, info)
|
|
40
45
|
|
|
46
|
+
@field_validator("external_id", mode="before")
|
|
47
|
+
@classmethod
|
|
48
|
+
def _resolve_optional_credentials(cls, v: object, info: LoaderValidationInfo) -> object:
|
|
49
|
+
return resolve_env_var(v, info, required=False)
|
|
50
|
+
|
|
41
51
|
|
|
42
52
|
class CspmConfig(BaseModel):
|
|
43
53
|
model_config = {"extra": "forbid"}
|
|
@@ -28,7 +28,7 @@ def _sub_env_var(m: re.Match[str]) -> str:
|
|
|
28
28
|
return value
|
|
29
29
|
|
|
30
30
|
|
|
31
|
-
def resolve_env_var(v: object, info: LoaderValidationInfo) -> object:
|
|
31
|
+
def resolve_env_var(v: object, info: LoaderValidationInfo, *, required: bool = True) -> object:
|
|
32
32
|
if not isinstance(v, str) or not (info.context or {}).get("resolve_env"):
|
|
33
33
|
return v
|
|
34
34
|
matches = list(_ENV_PLACEHOLDER.finditer(v))
|
|
@@ -39,6 +39,9 @@ def resolve_env_var(v: object, info: LoaderValidationInfo) -> object:
|
|
|
39
39
|
var_name = matches[0].group(1)
|
|
40
40
|
value = os.environ.get(var_name)
|
|
41
41
|
if value is None:
|
|
42
|
+
if required:
|
|
43
|
+
msg = f"environment variable '{var_name}' referenced in the scan config is not set"
|
|
44
|
+
raise ValueError(msg)
|
|
42
45
|
LOGGER.warning("env var '%s' not set; field will be None", var_name)
|
|
43
46
|
return None
|
|
44
47
|
LOGGER.info("env var '%s' set", var_name)
|
|
@@ -29,6 +29,19 @@ class GithubApp(BaseModel):
|
|
|
29
29
|
return resolve_env_var(v, info)
|
|
30
30
|
|
|
31
31
|
|
|
32
|
+
class AzureDevopsPat(BaseModel):
|
|
33
|
+
model_config = {"extra": "forbid"}
|
|
34
|
+
organization: str
|
|
35
|
+
pat_token: str = Field(repr=False)
|
|
36
|
+
environment_id: str | None = None
|
|
37
|
+
|
|
38
|
+
@field_validator("pat_token", mode="before")
|
|
39
|
+
@classmethod
|
|
40
|
+
def _resolve_credentials(cls, v: object, info: LoaderValidationInfo) -> object:
|
|
41
|
+
return resolve_env_var(v, info)
|
|
42
|
+
|
|
43
|
+
|
|
32
44
|
class SspmConfig(BaseModel):
|
|
33
45
|
model_config = {"extra": "forbid"}
|
|
34
46
|
github_credentials: list[GithubPat | GithubApp] = []
|
|
47
|
+
azure_devops_credentials: list[AzureDevopsPat] = []
|
{fluidattacks_core_models-14.2.0 → fluidattacks_core_models-14.4.0}/tests/test_loader_env.py
RENAMED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
from pathlib import Path
|
|
2
2
|
|
|
3
3
|
import pytest
|
|
4
|
-
from pydantic import BaseModel, field_validator
|
|
4
|
+
from pydantic import BaseModel, ValidationError, field_validator
|
|
5
5
|
|
|
6
|
+
from fluidattacks_core.models.scanners import AwsCredentials, AwsRole, CspmConfig
|
|
6
7
|
from fluidattacks_core.models.scanners.loader import (
|
|
7
8
|
LoaderValidationInfo,
|
|
8
9
|
build_config,
|
|
@@ -22,6 +23,16 @@ class _SecretFixture(BaseModel):
|
|
|
22
23
|
value: str | None = None
|
|
23
24
|
namespace: str = ""
|
|
24
25
|
|
|
26
|
+
@field_validator("value", mode="before")
|
|
27
|
+
@classmethod
|
|
28
|
+
def _resolve_value(cls, v: object, info: LoaderValidationInfo) -> object:
|
|
29
|
+
return resolve_env_var(v, info, required=False)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class _RequiredSecretFixture(BaseModel):
|
|
33
|
+
model_config = {"extra": "forbid"}
|
|
34
|
+
value: str
|
|
35
|
+
|
|
25
36
|
@field_validator("value", mode="before")
|
|
26
37
|
@classmethod
|
|
27
38
|
def _resolve_value(cls, v: object, info: LoaderValidationInfo) -> object:
|
|
@@ -45,7 +56,7 @@ def test_build_config_resolves_sensitive_field(
|
|
|
45
56
|
assert cfg.value == "resolved-value"
|
|
46
57
|
|
|
47
58
|
|
|
48
|
-
def
|
|
59
|
+
def test_build_config_missing_var_on_optional_field_resolves_to_none(
|
|
49
60
|
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
|
50
61
|
) -> None:
|
|
51
62
|
monkeypatch.delenv("TOKEN", raising=False)
|
|
@@ -54,6 +65,58 @@ def test_build_config_missing_var_resolves_to_none(
|
|
|
54
65
|
assert cfg.value is None
|
|
55
66
|
|
|
56
67
|
|
|
68
|
+
def test_build_config_set_var_on_required_field_resolves(
|
|
69
|
+
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
|
70
|
+
) -> None:
|
|
71
|
+
monkeypatch.setenv("TOKEN", "resolved-value")
|
|
72
|
+
path = _write(tmp_path, "value: ${TOKEN}\n")
|
|
73
|
+
cfg = build_config(path, _RequiredSecretFixture)
|
|
74
|
+
assert cfg.value == "resolved-value"
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def test_build_config_missing_var_on_required_field_names_the_variable(
|
|
78
|
+
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
|
79
|
+
) -> None:
|
|
80
|
+
monkeypatch.delenv("TOKEN", raising=False)
|
|
81
|
+
path = _write(tmp_path, "value: ${TOKEN}\n")
|
|
82
|
+
with pytest.raises(ValidationError) as exc_info:
|
|
83
|
+
build_config(path, _RequiredSecretFixture)
|
|
84
|
+
assert "environment variable 'TOKEN' referenced in the scan config is not set" in str(
|
|
85
|
+
exc_info.value
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def test_build_config_missing_var_on_cspm_external_id_resolves_to_none(
|
|
90
|
+
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
|
91
|
+
) -> None:
|
|
92
|
+
monkeypatch.delenv("CSPM_AWS_EXTERNAL_ID", raising=False)
|
|
93
|
+
path = _write(
|
|
94
|
+
tmp_path,
|
|
95
|
+
"aws_credentials:\n"
|
|
96
|
+
" - role: arn:aws:iam::123456789012:role/MyRole\n"
|
|
97
|
+
" external_id: ${CSPM_AWS_EXTERNAL_ID}\n",
|
|
98
|
+
)
|
|
99
|
+
role = build_config(path, CspmConfig).aws_credentials[0]
|
|
100
|
+
assert isinstance(role, AwsRole)
|
|
101
|
+
assert role.external_id is None
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def test_build_config_missing_var_on_cspm_session_token_resolves_to_none(
|
|
105
|
+
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
|
106
|
+
) -> None:
|
|
107
|
+
monkeypatch.delenv("CSPM_AWS_SESSION_TOKEN", raising=False)
|
|
108
|
+
path = _write(
|
|
109
|
+
tmp_path,
|
|
110
|
+
"aws_credentials:\n"
|
|
111
|
+
" - access_key_id: AKIA-fake-key\n"
|
|
112
|
+
" secret_access_key: secret\n"
|
|
113
|
+
" session_token: ${CSPM_AWS_SESSION_TOKEN}\n",
|
|
114
|
+
)
|
|
115
|
+
cred = build_config(path, CspmConfig).aws_credentials[0]
|
|
116
|
+
assert isinstance(cred, AwsCredentials)
|
|
117
|
+
assert cred.session_token is None
|
|
118
|
+
|
|
119
|
+
|
|
57
120
|
def test_build_config_non_sensitive_field_stays_literal(
|
|
58
121
|
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
|
59
122
|
) -> None:
|
|
@@ -87,6 +150,15 @@ def test_partial_missing_var_substitutes_empty_string(
|
|
|
87
150
|
assert cfg.value == "https:///api"
|
|
88
151
|
|
|
89
152
|
|
|
153
|
+
def test_partial_missing_var_on_required_field_substitutes_empty_string(
|
|
154
|
+
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
|
155
|
+
) -> None:
|
|
156
|
+
monkeypatch.delenv("HOST", raising=False)
|
|
157
|
+
path = _write(tmp_path, "value: https://${HOST}/api\n")
|
|
158
|
+
cfg = build_config(path, _RequiredSecretFixture)
|
|
159
|
+
assert cfg.value == "https:///api"
|
|
160
|
+
|
|
161
|
+
|
|
90
162
|
def test_multi_var_resolved(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
|
91
163
|
monkeypatch.setenv("A", "foo")
|
|
92
164
|
monkeypatch.setenv("B", "bar")
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
|
|
3
|
+
from fluidattacks_core.models.scanners import AzureDevopsPat, GithubPat, SspmConfig
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def test_azure_devops_credential_resolves_its_token(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
7
|
+
monkeypatch.setenv("AZURE_TOKEN", "resolved-value")
|
|
8
|
+
cfg = SspmConfig.model_validate(
|
|
9
|
+
{
|
|
10
|
+
"azure_devops_credentials": [
|
|
11
|
+
{"organization": "acme", "pat_token": "${AZURE_TOKEN}"},
|
|
12
|
+
],
|
|
13
|
+
},
|
|
14
|
+
context={"resolve_env": True},
|
|
15
|
+
)
|
|
16
|
+
assert cfg.azure_devops_credentials == [
|
|
17
|
+
AzureDevopsPat(organization="acme", pat_token="resolved-value"), # noqa: S106
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def test_azure_devops_and_github_credentials_stay_independent(
|
|
22
|
+
monkeypatch: pytest.MonkeyPatch,
|
|
23
|
+
) -> None:
|
|
24
|
+
monkeypatch.setenv("GH_TOKEN", "gh-value")
|
|
25
|
+
monkeypatch.setenv("AZ_TOKEN", "az-value")
|
|
26
|
+
cfg = SspmConfig.model_validate(
|
|
27
|
+
{
|
|
28
|
+
"github_credentials": [{"organization": "gh-org", "pat_token": "${GH_TOKEN}"}],
|
|
29
|
+
"azure_devops_credentials": [{"organization": "az-org", "pat_token": "${AZ_TOKEN}"}],
|
|
30
|
+
},
|
|
31
|
+
context={"resolve_env": True},
|
|
32
|
+
)
|
|
33
|
+
assert cfg.github_credentials == [
|
|
34
|
+
GithubPat(organization="gh-org", pat_token="gh-value"), # noqa: S106
|
|
35
|
+
]
|
|
36
|
+
assert cfg.azure_devops_credentials == [
|
|
37
|
+
AzureDevopsPat(organization="az-org", pat_token="az-value"), # noqa: S106
|
|
38
|
+
]
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
use_flake ".#python311.devShell"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fluidattacks_core_models-14.2.0 → fluidattacks_core_models-14.4.0}/tests/test_model_scanner.py
RENAMED
|
File without changes
|
{fluidattacks_core_models-14.2.0 → fluidattacks_core_models-14.4.0}/tests/test_root_scanner.py
RENAMED
|
File without changes
|
{fluidattacks_core_models-14.2.0 → fluidattacks_core_models-14.4.0}/tests/test_state_machines.py
RENAMED
|
File without changes
|