sec-audit-rules 0.1.0a2__py3-none-any.whl
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.
- sec_audit/enforcement/__init__.py +41 -0
- sec_audit/enforcement/actions.py +88 -0
- sec_audit/enforcement/blocks.py +62 -0
- sec_audit/enforcement/config.py +99 -0
- sec_audit/enforcement/policies.py +65 -0
- sec_audit/integrations/wazuh/__init__.py +3 -0
- sec_audit/integrations/wazuh/api.py +69 -0
- sec_audit/integrations/wazuh/rules/0375-sec-audit.xml +7 -0
- sec_audit/integrations/wazuh/rules/sigma/audit-rule-match.yml +8 -0
- sec_audit/rules/__init__.py +50 -0
- sec_audit/rules/base.py +228 -0
- sec_audit/rules/builtins/__init__.py +16 -0
- sec_audit/rules/builtins/brute_force.py +87 -0
- sec_audit/rules/builtins/model_changes.py +62 -0
- sec_audit/rules/builtins/proxy.py +69 -0
- sec_audit/rules/builtins/repeated_errors.py +39 -0
- sec_audit/rules/builtins/request_body.py +69 -0
- sec_audit/rules/builtins/routes.py +74 -0
- sec_audit/rules/config.py +44 -0
- sec_audit/rules/engine.py +225 -0
- sec_audit/rules/events.py +374 -0
- sec_audit/rules/history.py +155 -0
- sec_audit/rules/result_sinks/__init__.py +1 -0
- sec_audit/rules/scopes.py +147 -0
- sec_audit/rules/stores/__init__.py +32 -0
- sec_audit/rules/stores/counters.py +159 -0
- sec_audit/rules/stores/history.py +106 -0
- sec_audit/rules/stores/memory.py +4 -0
- sec_audit/rules/stores/redis.py +223 -0
- sec_audit_rules-0.1.0a2.dist-info/METADATA +92 -0
- sec_audit_rules-0.1.0a2.dist-info/RECORD +33 -0
- sec_audit_rules-0.1.0a2.dist-info/WHEEL +4 -0
- sec_audit_rules-0.1.0a2.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
from sec_audit.enforcement.actions import (
|
|
2
|
+
ALERT_SEVERITY,
|
|
3
|
+
BLOCKING_ACTIONS,
|
|
4
|
+
DEFAULT_BLOCK_SCOPES,
|
|
5
|
+
PERSISTENT_ACTIONS,
|
|
6
|
+
TEMPORARY_ACTIONS,
|
|
7
|
+
RuleAction,
|
|
8
|
+
effective_action_ttl,
|
|
9
|
+
resolve_rule_action,
|
|
10
|
+
)
|
|
11
|
+
from sec_audit.enforcement.blocks import (
|
|
12
|
+
BlockEntry,
|
|
13
|
+
BlockScope,
|
|
14
|
+
BlockStore,
|
|
15
|
+
)
|
|
16
|
+
from sec_audit.enforcement.config import EnforcementAuditConfig
|
|
17
|
+
from sec_audit.enforcement.policies import (
|
|
18
|
+
EnforcementDecision,
|
|
19
|
+
EnforcementPolicy,
|
|
20
|
+
SeverityEnforcementPolicy,
|
|
21
|
+
highest_severity_match,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
__all__ = [
|
|
25
|
+
'ALERT_SEVERITY',
|
|
26
|
+
'BLOCKING_ACTIONS',
|
|
27
|
+
'BlockEntry',
|
|
28
|
+
'BlockScope',
|
|
29
|
+
'BlockStore',
|
|
30
|
+
'DEFAULT_BLOCK_SCOPES',
|
|
31
|
+
'EnforcementAuditConfig',
|
|
32
|
+
'EnforcementDecision',
|
|
33
|
+
'EnforcementPolicy',
|
|
34
|
+
'PERSISTENT_ACTIONS',
|
|
35
|
+
'RuleAction',
|
|
36
|
+
'SeverityEnforcementPolicy',
|
|
37
|
+
'TEMPORARY_ACTIONS',
|
|
38
|
+
'effective_action_ttl',
|
|
39
|
+
'highest_severity_match',
|
|
40
|
+
'resolve_rule_action',
|
|
41
|
+
]
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import Mapping
|
|
5
|
+
|
|
6
|
+
from sec_audit.enforcement.policies import EnforcementDecision
|
|
7
|
+
from sec_audit.rules.base import RuleMatch
|
|
8
|
+
|
|
9
|
+
TEMPORARY_ACTIONS = {'temp_block'}
|
|
10
|
+
PERSISTENT_ACTIONS = {'persist_block'}
|
|
11
|
+
BLOCKING_ACTIONS = {'block', 'temp_block', 'persist_block'}
|
|
12
|
+
ALERT_SEVERITY = 4
|
|
13
|
+
DEFAULT_BLOCK_SCOPES = ('ip',)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass(frozen=True)
|
|
17
|
+
class RuleAction:
|
|
18
|
+
action: str
|
|
19
|
+
ttl: int | None = None
|
|
20
|
+
scopes: tuple[str, ...] = DEFAULT_BLOCK_SCOPES
|
|
21
|
+
status_code: int | None = None
|
|
22
|
+
message: str | None = None
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _configured_rule_action(spec: object) -> RuleAction:
|
|
26
|
+
if isinstance(spec, str):
|
|
27
|
+
return RuleAction(action=spec)
|
|
28
|
+
data = dict(spec) if isinstance(spec, Mapping) else {}
|
|
29
|
+
scopes = data.get('scopes') or DEFAULT_BLOCK_SCOPES
|
|
30
|
+
if isinstance(scopes, str):
|
|
31
|
+
scopes = (scopes,)
|
|
32
|
+
return RuleAction(
|
|
33
|
+
action=str(data.get('action') or 'observe'),
|
|
34
|
+
ttl=data.get('ttl'),
|
|
35
|
+
scopes=tuple(str(scope) for scope in scopes),
|
|
36
|
+
status_code=data.get('status_code'),
|
|
37
|
+
message=data.get('message'),
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _match_block_ttl(match: RuleMatch, default_ttl: int | None) -> int | None:
|
|
42
|
+
ttl = match.metadata.get('block_ttl', match.metadata.get('ttl'))
|
|
43
|
+
if ttl is None and match.decision == 'temp_block':
|
|
44
|
+
ttl = default_ttl or 300
|
|
45
|
+
return int(ttl) if ttl is not None else None
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def effective_action_ttl(
|
|
49
|
+
rule_action: RuleAction,
|
|
50
|
+
match: RuleMatch,
|
|
51
|
+
default_ttl: int | None,
|
|
52
|
+
) -> int | None:
|
|
53
|
+
return rule_action.ttl or _match_block_ttl(match, default_ttl) or default_ttl
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def resolve_rule_action(
|
|
57
|
+
match: RuleMatch,
|
|
58
|
+
*,
|
|
59
|
+
configured_actions: Mapping[str, object],
|
|
60
|
+
block_rules: Mapping[str, int],
|
|
61
|
+
default_ttl: int | None,
|
|
62
|
+
policy_decision: EnforcementDecision | None = None,
|
|
63
|
+
default_action: str = 'observe',
|
|
64
|
+
) -> RuleAction:
|
|
65
|
+
configured = configured_actions.get(match.rule_name)
|
|
66
|
+
if configured is not None:
|
|
67
|
+
return _configured_rule_action(configured)
|
|
68
|
+
if match.rule_name in block_rules:
|
|
69
|
+
return RuleAction(action='temp_block', ttl=block_rules[match.rule_name])
|
|
70
|
+
if match.decision in BLOCKING_ACTIONS or match.decision in {'alert', 'observe'}:
|
|
71
|
+
return RuleAction(
|
|
72
|
+
action=str(match.decision),
|
|
73
|
+
ttl=_match_block_ttl(match, default_ttl),
|
|
74
|
+
)
|
|
75
|
+
if policy_decision is not None and not policy_decision.allowed:
|
|
76
|
+
return RuleAction(
|
|
77
|
+
action='block',
|
|
78
|
+
status_code=policy_decision.status_code,
|
|
79
|
+
message=policy_decision.message,
|
|
80
|
+
)
|
|
81
|
+
if default_action == 'alert':
|
|
82
|
+
return RuleAction(action='alert')
|
|
83
|
+
if default_action in BLOCKING_ACTIONS:
|
|
84
|
+
return RuleAction(
|
|
85
|
+
action=str(default_action),
|
|
86
|
+
ttl=_match_block_ttl(match, default_ttl),
|
|
87
|
+
)
|
|
88
|
+
return RuleAction(action='observe')
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass, field
|
|
4
|
+
from datetime import datetime
|
|
5
|
+
from types import MappingProxyType
|
|
6
|
+
from typing import Mapping, Protocol, Sequence
|
|
7
|
+
|
|
8
|
+
DEFAULT_BLOCK_MESSAGE = 'Request blocked by audit enforcement policy'
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@dataclass(frozen=True)
|
|
12
|
+
class BlockScope:
|
|
13
|
+
scope_type: str
|
|
14
|
+
scope_value: str
|
|
15
|
+
|
|
16
|
+
def __post_init__(self) -> None:
|
|
17
|
+
scope_type = str(self.scope_type).strip()
|
|
18
|
+
scope_value = str(self.scope_value).strip()
|
|
19
|
+
if not scope_type:
|
|
20
|
+
raise ValueError('scope_type cannot be empty.')
|
|
21
|
+
if not scope_value:
|
|
22
|
+
raise ValueError('scope_value cannot be empty.')
|
|
23
|
+
object.__setattr__(self, 'scope_type', scope_type)
|
|
24
|
+
object.__setattr__(self, 'scope_value', scope_value)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@dataclass(frozen=True)
|
|
28
|
+
class BlockEntry:
|
|
29
|
+
scope: BlockScope
|
|
30
|
+
reason: str = ''
|
|
31
|
+
rule_name: str = ''
|
|
32
|
+
status_code: int = 429
|
|
33
|
+
message: str = DEFAULT_BLOCK_MESSAGE
|
|
34
|
+
created_at: datetime | None = None
|
|
35
|
+
expires_at: datetime | None = None
|
|
36
|
+
revoked_at: datetime | None = None
|
|
37
|
+
metadata: Mapping[str, object] | None = field(default=None)
|
|
38
|
+
|
|
39
|
+
def __post_init__(self) -> None:
|
|
40
|
+
object.__setattr__(
|
|
41
|
+
self, 'metadata', MappingProxyType(dict(self.metadata or {}))
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class BlockStore(Protocol):
|
|
46
|
+
def block(
|
|
47
|
+
self,
|
|
48
|
+
scope: BlockScope,
|
|
49
|
+
*,
|
|
50
|
+
reason: str = '',
|
|
51
|
+
rule_name: str = '',
|
|
52
|
+
status_code: int = 429,
|
|
53
|
+
message: str = DEFAULT_BLOCK_MESSAGE,
|
|
54
|
+
ttl: int | None = None,
|
|
55
|
+
metadata: Mapping[str, object] | None = None,
|
|
56
|
+
) -> BlockEntry: ...
|
|
57
|
+
|
|
58
|
+
def unblock(self, scope: BlockScope, *, reason: str = '') -> int: ...
|
|
59
|
+
|
|
60
|
+
def get_active(self, scope: BlockScope) -> BlockEntry | None: ...
|
|
61
|
+
|
|
62
|
+
def first_active(self, scopes: Sequence[BlockScope]) -> BlockEntry | None: ...
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass, field
|
|
4
|
+
from typing import Mapping
|
|
5
|
+
|
|
6
|
+
from sec_audit.core.config_validation import int_value, str_value
|
|
7
|
+
from sec_audit.core.exceptions import AuditConfigurationError
|
|
8
|
+
|
|
9
|
+
RULE_ACTIONS = {'observe', 'alert', 'block', 'temp_block', 'persist_block'}
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass(frozen=True)
|
|
13
|
+
class EnforcementAuditConfig:
|
|
14
|
+
enforcement_block_severity: int | None = None
|
|
15
|
+
enforcement_status_code: int = 429
|
|
16
|
+
enforcement_message: str = 'Request blocked by audit enforcement policy'
|
|
17
|
+
block_cache_ttl: int = 300
|
|
18
|
+
block_rules: dict[str, int] = field(default_factory=dict)
|
|
19
|
+
rule_actions: dict[str, object] = field(default_factory=dict)
|
|
20
|
+
|
|
21
|
+
def __post_init__(self) -> None:
|
|
22
|
+
if self.enforcement_block_severity is not None:
|
|
23
|
+
severity = int_value(
|
|
24
|
+
'enforcement_block_severity', self.enforcement_block_severity
|
|
25
|
+
)
|
|
26
|
+
if severity < 0 or severity > 10:
|
|
27
|
+
raise AuditConfigurationError(
|
|
28
|
+
'enforcement_block_severity must be None or between 0 and 10.'
|
|
29
|
+
)
|
|
30
|
+
object.__setattr__(self, 'enforcement_block_severity', severity)
|
|
31
|
+
status_code = int_value('enforcement_status_code', self.enforcement_status_code)
|
|
32
|
+
if status_code < 100 or status_code > 599:
|
|
33
|
+
raise AuditConfigurationError(
|
|
34
|
+
'enforcement_status_code must be an HTTP status code.'
|
|
35
|
+
)
|
|
36
|
+
object.__setattr__(self, 'enforcement_status_code', status_code)
|
|
37
|
+
object.__setattr__(
|
|
38
|
+
self,
|
|
39
|
+
'enforcement_message',
|
|
40
|
+
str_value('enforcement_message', self.enforcement_message),
|
|
41
|
+
)
|
|
42
|
+
block_cache_ttl = int_value('block_cache_ttl', self.block_cache_ttl)
|
|
43
|
+
if block_cache_ttl < 0:
|
|
44
|
+
raise AuditConfigurationError(
|
|
45
|
+
'block_cache_ttl must be greater than or equal to 0.'
|
|
46
|
+
)
|
|
47
|
+
object.__setattr__(self, 'block_cache_ttl', block_cache_ttl)
|
|
48
|
+
object.__setattr__(self, 'block_rules', _validate_block_rules(self.block_rules))
|
|
49
|
+
object.__setattr__(
|
|
50
|
+
self, 'rule_actions', _validate_rule_actions(self.rule_actions)
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _validate_block_rules(value: object) -> dict[str, int]:
|
|
55
|
+
if not isinstance(value, Mapping):
|
|
56
|
+
raise AuditConfigurationError('block_rules must be a mapping.')
|
|
57
|
+
parsed = {}
|
|
58
|
+
for rule_name, ttl in value.items():
|
|
59
|
+
parsed[str(rule_name)] = int_value('block_rules TTL', ttl)
|
|
60
|
+
if parsed[str(rule_name)] <= 0:
|
|
61
|
+
raise AuditConfigurationError('block_rules TTLs must be positive.')
|
|
62
|
+
return parsed
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _validate_rule_actions(value: object) -> dict[str, object]:
|
|
66
|
+
if not isinstance(value, Mapping):
|
|
67
|
+
raise AuditConfigurationError('rule_actions must be a mapping.')
|
|
68
|
+
parsed = {}
|
|
69
|
+
for rule_name, spec in value.items():
|
|
70
|
+
if isinstance(spec, str):
|
|
71
|
+
normalized = {'action': spec}
|
|
72
|
+
elif isinstance(spec, Mapping):
|
|
73
|
+
normalized = dict(spec)
|
|
74
|
+
else:
|
|
75
|
+
raise AuditConfigurationError(
|
|
76
|
+
'rule_actions values must be action strings or mappings.'
|
|
77
|
+
)
|
|
78
|
+
action = normalized.get('action')
|
|
79
|
+
if not isinstance(action, str) or action not in RULE_ACTIONS:
|
|
80
|
+
raise AuditConfigurationError(
|
|
81
|
+
f'rule_actions action for {rule_name!r} must be one of: '
|
|
82
|
+
f'{", ".join(sorted(RULE_ACTIONS))}.'
|
|
83
|
+
)
|
|
84
|
+
if 'ttl' in normalized and normalized['ttl'] is not None:
|
|
85
|
+
normalized['ttl'] = int_value('rule_actions ttl', normalized['ttl'])
|
|
86
|
+
if normalized['ttl'] <= 0:
|
|
87
|
+
raise AuditConfigurationError('rule_actions ttl must be positive.')
|
|
88
|
+
if 'status_code' in normalized and normalized['status_code'] is not None:
|
|
89
|
+
normalized['status_code'] = int_value(
|
|
90
|
+
'rule_actions status_code', normalized['status_code']
|
|
91
|
+
)
|
|
92
|
+
if normalized['status_code'] < 100 or normalized['status_code'] > 599:
|
|
93
|
+
raise AuditConfigurationError(
|
|
94
|
+
'rule_actions status_code must be an HTTP status code.'
|
|
95
|
+
)
|
|
96
|
+
if 'scopes' in normalized and isinstance(normalized['scopes'], str):
|
|
97
|
+
raise AuditConfigurationError('rule_actions scopes must be a sequence.')
|
|
98
|
+
parsed[str(rule_name)] = normalized
|
|
99
|
+
return parsed
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import Mapping, Protocol, Sequence
|
|
5
|
+
|
|
6
|
+
from sec_audit.rules.base import RuleMatch
|
|
7
|
+
from sec_audit.rules.events import RuleEvent
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@dataclass(frozen=True)
|
|
11
|
+
class EnforcementDecision:
|
|
12
|
+
allowed: bool = True
|
|
13
|
+
status_code: int = 429
|
|
14
|
+
message: str = 'Request blocked by audit enforcement policy'
|
|
15
|
+
reason: str | None = None
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class EnforcementPolicy(Protocol):
|
|
19
|
+
def decide(
|
|
20
|
+
self,
|
|
21
|
+
event: RuleEvent | Mapping[str, object],
|
|
22
|
+
matches: Sequence[RuleMatch],
|
|
23
|
+
) -> EnforcementDecision: ...
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def highest_severity_match(matches: Sequence[RuleMatch]) -> RuleMatch | None:
|
|
27
|
+
best = None
|
|
28
|
+
for match in matches:
|
|
29
|
+
if best is None or match.severity > best.severity:
|
|
30
|
+
best = match
|
|
31
|
+
return best
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class SeverityEnforcementPolicy:
|
|
35
|
+
def __init__(
|
|
36
|
+
self,
|
|
37
|
+
*,
|
|
38
|
+
block_severity: int | None = 8,
|
|
39
|
+
status_code: int = 429,
|
|
40
|
+
message: str = 'Request blocked by audit enforcement policy',
|
|
41
|
+
) -> None:
|
|
42
|
+
self.block_severity = (
|
|
43
|
+
int(block_severity) if block_severity is not None else None
|
|
44
|
+
)
|
|
45
|
+
self.status_code = int(status_code)
|
|
46
|
+
self.message = message
|
|
47
|
+
|
|
48
|
+
def decide(
|
|
49
|
+
self,
|
|
50
|
+
event: RuleEvent | Mapping[str, object],
|
|
51
|
+
matches: Sequence[RuleMatch],
|
|
52
|
+
) -> EnforcementDecision:
|
|
53
|
+
if self.block_severity is None:
|
|
54
|
+
return EnforcementDecision()
|
|
55
|
+
triggering = highest_severity_match(
|
|
56
|
+
[match for match in matches if match.severity >= self.block_severity]
|
|
57
|
+
)
|
|
58
|
+
if triggering is None:
|
|
59
|
+
return EnforcementDecision()
|
|
60
|
+
return EnforcementDecision(
|
|
61
|
+
allowed=False,
|
|
62
|
+
status_code=self.status_code,
|
|
63
|
+
message=self.message,
|
|
64
|
+
reason=triggering.rule_name,
|
|
65
|
+
)
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import time
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class WazuhAPISource:
|
|
5
|
+
def __init__(
|
|
6
|
+
self,
|
|
7
|
+
*,
|
|
8
|
+
base_url: str = 'https://localhost:55000',
|
|
9
|
+
username: str = 'admin',
|
|
10
|
+
password: str = 'admin',
|
|
11
|
+
verify_ssl: bool = False,
|
|
12
|
+
) -> None:
|
|
13
|
+
import httpx
|
|
14
|
+
|
|
15
|
+
self.base_url = base_url.rstrip('/')
|
|
16
|
+
self.username = username
|
|
17
|
+
self.password = password
|
|
18
|
+
self._session = httpx.Client(verify=verify_ssl)
|
|
19
|
+
self._token = None
|
|
20
|
+
self._token_expiry = 0.0
|
|
21
|
+
|
|
22
|
+
def _authenticate(self) -> bool:
|
|
23
|
+
now = time.time()
|
|
24
|
+
if self._token and now < self._token_expiry - 30:
|
|
25
|
+
return True
|
|
26
|
+
try:
|
|
27
|
+
resp = self._session.post(
|
|
28
|
+
f'{self.base_url}/security/user/authenticate',
|
|
29
|
+
json={},
|
|
30
|
+
auth=(self.username, self.password),
|
|
31
|
+
timeout=3,
|
|
32
|
+
)
|
|
33
|
+
resp.raise_for_status()
|
|
34
|
+
self._token = resp.json()['data']['token']
|
|
35
|
+
self._token_expiry = now + 3600
|
|
36
|
+
return True
|
|
37
|
+
except Exception:
|
|
38
|
+
return False
|
|
39
|
+
|
|
40
|
+
def is_available(self) -> bool:
|
|
41
|
+
return self._authenticate()
|
|
42
|
+
|
|
43
|
+
def request(self, method: str, path: str, **kwargs):
|
|
44
|
+
if not self._authenticate():
|
|
45
|
+
return None
|
|
46
|
+
headers = kwargs.pop('headers', {})
|
|
47
|
+
headers['Authorization'] = f'Bearer {self._token}'
|
|
48
|
+
try:
|
|
49
|
+
resp = self._session.request(
|
|
50
|
+
method,
|
|
51
|
+
f'{self.base_url}{path}',
|
|
52
|
+
headers=headers,
|
|
53
|
+
timeout=kwargs.pop('timeout', 15),
|
|
54
|
+
**kwargs,
|
|
55
|
+
)
|
|
56
|
+
resp.raise_for_status()
|
|
57
|
+
return resp.json()
|
|
58
|
+
except Exception:
|
|
59
|
+
return None
|
|
60
|
+
|
|
61
|
+
def get_agents(self):
|
|
62
|
+
result = self.request(
|
|
63
|
+
'GET', '/agents', params={'select': 'id,name,status,lastKeepAlive'}
|
|
64
|
+
)
|
|
65
|
+
return (result or {}).get('data', {})
|
|
66
|
+
|
|
67
|
+
def get_manager_info(self):
|
|
68
|
+
result = self.request('GET', '/manager/info')
|
|
69
|
+
return (result or {}).get('data', {})
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
from sec_audit.rules.base import (
|
|
2
|
+
ContextRequirements,
|
|
3
|
+
Rule,
|
|
4
|
+
RuleContext,
|
|
5
|
+
RuleMatch,
|
|
6
|
+
ScopeContext,
|
|
7
|
+
ScopedHistoryReader,
|
|
8
|
+
)
|
|
9
|
+
from sec_audit.rules.builtins import (
|
|
10
|
+
BruteForceLoginRule,
|
|
11
|
+
LoginThrottleRule,
|
|
12
|
+
RepeatedClientErrorRule,
|
|
13
|
+
RepeatedRouteRule,
|
|
14
|
+
RequestBodyThresholdRule,
|
|
15
|
+
SensitiveFieldChangeRule,
|
|
16
|
+
SuspiciousProxyHeaderRule,
|
|
17
|
+
)
|
|
18
|
+
from sec_audit.rules.config import RulesAuditConfig
|
|
19
|
+
from sec_audit.rules.engine import RuleEngine
|
|
20
|
+
from sec_audit.rules.events import RuleEvent, SummaryKey
|
|
21
|
+
from sec_audit.rules.history import (
|
|
22
|
+
HistoryScopeExtractor,
|
|
23
|
+
ScopeKey,
|
|
24
|
+
build_history_scope_extractors,
|
|
25
|
+
extract_scope_keys,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
__all__ = [
|
|
29
|
+
'BruteForceLoginRule',
|
|
30
|
+
'ContextRequirements',
|
|
31
|
+
'LoginThrottleRule',
|
|
32
|
+
'RepeatedClientErrorRule',
|
|
33
|
+
'RepeatedRouteRule',
|
|
34
|
+
'RequestBodyThresholdRule',
|
|
35
|
+
'Rule',
|
|
36
|
+
'RuleContext',
|
|
37
|
+
'RuleEngine',
|
|
38
|
+
'RuleEvent',
|
|
39
|
+
'RuleMatch',
|
|
40
|
+
'RulesAuditConfig',
|
|
41
|
+
'ScopeContext',
|
|
42
|
+
'ScopeKey',
|
|
43
|
+
'ScopedHistoryReader',
|
|
44
|
+
'SensitiveFieldChangeRule',
|
|
45
|
+
'SuspiciousProxyHeaderRule',
|
|
46
|
+
'SummaryKey',
|
|
47
|
+
'HistoryScopeExtractor',
|
|
48
|
+
'build_history_scope_extractors',
|
|
49
|
+
'extract_scope_keys',
|
|
50
|
+
]
|