fix: redact a data field in Action secret responses; test: secret echo suite
This commit is contained in:
1 parent
fef80a5b21
commit
421a6507c0
2 files changed
+288
-3
No files matched your search
@@ -14,7 +14,8 @@ Security properties (all covered by tests):
|
||||
OAuth client secret, ``extra_secrets`` and secret values submitted in a
|
||||
request body are replaced with ``[REDACTED]`` in every result, always;
|
||||
* secret-bearing response fields (:data:`~.encoding.SENSITIVE_RESPONSE_KEYS`, for example a
|
||||
webhook's stored ``authorization_header``) are redacted by key name in every result except
|
||||
webhook's stored ``authorization_header``, and ``data`` on Action secret operations) are
|
||||
redacted by key name in every result except
|
||||
those of :data:`CREDENTIAL_ISSUING_OPERATIONS`, whatever ``SECRET_DETECTION_MODE`` says;
|
||||
* secret-pattern masking/blocking follows ``SECRET_DETECTION_MODE``, except for
|
||||
:data:`CREDENTIAL_ISSUING_OPERATIONS` whose purpose is to return a freshly
|
||||
@@ -26,6 +27,7 @@ Security properties (all covered by tests):
|
||||
from __future__ import annotations
|
||||
|
||||
import base64
|
||||
import itertools
|
||||
import json
|
||||
from collections.abc import Iterable, Mapping
|
||||
from dataclasses import dataclass, field
|
||||
@@ -171,6 +173,8 @@ class _Call:
|
||||
sudo: str | None = None
|
||||
paginated: bool = False
|
||||
credential_issuing: bool = False
|
||||
#: Action secret operations: a ``data`` field in the response is a secret value.
|
||||
action_secret: bool = False
|
||||
header_data: str | None = None
|
||||
call_secrets: tuple[str, ...] = ()
|
||||
|
||||
@@ -183,6 +187,13 @@ class _Call:
|
||||
return next((value for key, value in self.query if key == name), None)
|
||||
|
||||
|
||||
def _is_action_secret_path(segments: list[str]) -> bool:
|
||||
"""True for ``.../actions/secrets[/...]`` raw paths (an Action secrets endpoint)."""
|
||||
return any(
|
||||
first == "actions" and second == "secrets" for first, second in itertools.pairwise(segments)
|
||||
)
|
||||
|
||||
|
||||
def _classify(media_type: str) -> str:
|
||||
"""Return ``json``, ``text`` or ``binary`` for a response media type."""
|
||||
if not media_type or media_type == "application/json" or media_type.endswith("+json"):
|
||||
@@ -358,6 +369,7 @@ class GiteaExecutor:
|
||||
sudo=sudo,
|
||||
paginated=operation.paginated,
|
||||
credential_issuing=credential_issuing,
|
||||
action_secret="Secret" in operation.operation_id,
|
||||
header_data=HEADER_DATA_OPERATIONS.get(operation.operation_id),
|
||||
call_secrets=tuple(
|
||||
sensitive_body_values(submitted, secret_data="Secret" in operation.operation_id)
|
||||
@@ -415,6 +427,7 @@ class GiteaExecutor:
|
||||
call.body = RequestBody(json=body, has_json=True)
|
||||
segments = call.display_path.split("/")
|
||||
call.call_secrets = tuple(sensitive_body_values(body, secret_data="secrets" in segments))
|
||||
call.action_secret = _is_action_secret_path(segments)
|
||||
call.paginated = any(key in {"page", "limit"} for key, _ in call.query)
|
||||
return await self._send(call, identity)
|
||||
|
||||
@@ -624,8 +637,15 @@ class GiteaExecutor:
|
||||
payload = redact_exact(payload, secrets)
|
||||
if not exempt and "data" in payload:
|
||||
# Stored secrets (webhook authorization headers, ...) by field name: pattern
|
||||
# masking cannot recognise them and SECRET_DETECTION_MODE may be off.
|
||||
payload = {**payload, "data": redact_fields(payload["data"], SENSITIVE_RESPONSE_KEYS)}
|
||||
# masking cannot recognise them and SECRET_DETECTION_MODE may be off. Gitea
|
||||
# never returns an Action secret's value; should a response carry a ``data``
|
||||
# field on a secret operation, it is the value and is redacted too.
|
||||
keys = (
|
||||
SENSITIVE_RESPONSE_KEYS | {"data"}
|
||||
if call.action_secret
|
||||
else SENSITIVE_RESPONSE_KEYS
|
||||
)
|
||||
payload = {**payload, "data": redact_fields(payload["data"], keys)}
|
||||
mode = self._settings.secret_detection_mode
|
||||
if mode != "off" and not exempt:
|
||||
if mode == "block":
|
||||
|
||||
Reference in new issue
Block a user