Skip to content

Commit 813a29c

Browse files
committed
fix(serverless): fully redact secrets and remove startup import cycle
1 parent bc89d20 commit 813a29c

5 files changed

Lines changed: 40 additions & 25 deletions

File tree

‎runpod/_health/__init__.py‎

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,20 @@
1-
"""Worker health checks independent of the serverless import tree."""
1+
"""Worker-process identity shared by startup and health checks."""
2+
3+
import os
4+
import sys
5+
6+
# Launchers may set this to the PID of the Python handler process before exec.
7+
# A generic container-level boolean would also authorize unrelated processes.
8+
WORKER_PID_ENV = "RUNPOD_FITNESS_WORKER_PID"
9+
10+
11+
def is_worker_process() -> bool:
12+
"""Require explicit launcher identity and exclude local/API test invocations."""
13+
return (
14+
os.environ.get(WORKER_PID_ENV) == str(os.getpid())
15+
and bool(os.environ.get("RUNPOD_WEBHOOK_GET_JOB"))
16+
and not any(
17+
arg.split("=", 1)[0] in ("--test_input", "--rp_serve_api")
18+
for arg in sys.argv[1:]
19+
)
20+
)

‎runpod/_health/fitness.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
from collections.abc import Callable
2121

2222
from runpod._logger import RunPodLogger
23-
from runpod._startup import is_worker_process
23+
from . import is_worker_process
2424

2525
log = RunPodLogger()
2626

‎runpod/_logger.py‎

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -110,13 +110,11 @@ def log(self, message, message_level="INFO", job_id=None):
110110
return
111111

112112
def secret(self, secret_name, secret):
113-
"""
114-
Censors secrets for logging.
115-
Replaces everything except the first and last characters with *
116-
"""
117-
secret = str(secret)
118-
redacted_secret = secret[0] + "*" * (len(secret) - 2) + secret[-1]
119-
self.info(f"{secret_name}: {redacted_secret}")
113+
"""Log the secret's name without exposing its value or length."""
114+
# Even a one-character value must be completely redacted. Do not call
115+
# str(secret): custom objects may reveal sensitive data or raise.
116+
self.info(f"{secret_name}: [REDACTED]")
117+
120118

121119
def debug(self, message, request_id: Optional[str] = None):
122120
"""

‎runpod/_startup.py‎

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,10 @@
11
"""Process-scoped startup gate; safe to import without loading serverless."""
22

3-
import os
43
import sys
54

6-
# Launchers may set this to the PID of the Python handler process before exec.
7-
# A generic container-level boolean would also authorize unrelated processes.
8-
WORKER_PID_ENV = "RUNPOD_FITNESS_WORKER_PID"
5+
from ._health import WORKER_PID_ENV, is_worker_process
96

10-
11-
def is_worker_process() -> bool:
12-
"""Require explicit launcher identity and exclude local/API test invocations."""
13-
return (
14-
os.environ.get(WORKER_PID_ENV) == str(os.getpid())
15-
and bool(os.environ.get("RUNPOD_WEBHOOK_GET_JOB"))
16-
and not any(
17-
arg.split("=", 1)[0] in ("--test_input", "--rp_serve_api")
18-
for arg in sys.argv[1:]
19-
)
20-
)
7+
__all__ = ["WORKER_PID_ENV", "is_worker_process", "run_import_checks"]
218

229

2310
def run_import_checks() -> None:

‎tests/test_serverless/test_modules/test_logger.py‎

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,9 +105,20 @@ def test_log_secret(self):
105105
with patch("runpod.serverless.modules.rp_logger.RunPodLogger.log") as mock_log:
106106
self.logger.secret("test_secret", "test_secret_value")
107107
mock_log.assert_called_once_with(
108-
"test_secret: t***************e", "INFO", None
108+
"test_secret: [REDACTED]", "INFO", None
109109
)
110110

111+
def test_secret_redacts_short_empty_and_object_values(self):
112+
class Sensitive:
113+
def __str__(self):
114+
raise AssertionError("A secret must not be converted to text")
115+
116+
for value in ("", "a", "ab", "long-secret", None, Sensitive()):
117+
with self.subTest(value_type=type(value).__name__):
118+
with patch.object(self.logger, "log") as mock_log:
119+
self.logger.secret("credential", value)
120+
mock_log.assert_called_once_with("credential: [REDACTED]", "INFO", None)
121+
111122
def test_log_tip(self):
112123
"""
113124
Tests that the tip method logs a tip.

0 commit comments

Comments
 (0)