Skip to content

Commit 5a83b4e

Browse files
committed
Update ProcessResourceDetector to not set cli args by default
1 parent 50912be commit 5a83b4e

3 files changed

Lines changed: 100 additions & 14 deletions

File tree

.changelog/5364.fixed

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
`opentelemetry-sdk`: `ProcessResourceDetector` no longer collects or emits
2+
`process.command_args` and `process.command_line` by default. Users who depend
3+
on these resource attributes must pass `include_command_args=True`.

opentelemetry-sdk/src/opentelemetry/sdk/resources/__init__.py

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -321,7 +321,24 @@ def detect(self) -> "Resource":
321321

322322

323323
class ProcessResourceDetector(ResourceDetector):
324-
# pylint: disable=no-self-use
324+
"""Detect process resource attributes.
325+
326+
Args:
327+
raise_on_error: Raise errors from detection instead of logging them.
328+
include_command_args: Include ``process.command_args`` and
329+
``process.command_line``. These attributes can contain sensitive
330+
command-line values, so they are excluded by default.
331+
"""
332+
333+
def __init__(
334+
self,
335+
raise_on_error: bool = False,
336+
*,
337+
include_command_args: bool = False,
338+
) -> None:
339+
super().__init__(raise_on_error=raise_on_error)
340+
self._include_command_args = include_command_args
341+
325342
def detect(self) -> "Resource":
326343
_runtime_version = ".".join(
327344
map(
@@ -337,27 +354,24 @@ def detect(self) -> "Resource":
337354
_process_pid = os.getpid()
338355
_process_executable_name = sys.executable
339356
_process_executable_path = os.path.dirname(_process_executable_name)
340-
# Use sys.orig_argv, which preserves the original arguments received
341-
# by the interpreter. This correctly captures ``python -m <module>``
342-
# invocations where sys.argv is rewritten to the resolved module path
343-
# and the ``-m <module>`` information is lost. sys.orig_argv also
344-
# aligns with /proc/<pid>/cmdline, which the OTel semantic
345-
# conventions reference for these attributes.
346-
_process_argv = list(sys.orig_argv)
347-
_process_command = _process_argv[0] if _process_argv else ""
348-
_process_command_line = " ".join(_process_argv)
349-
_process_command_args = _process_argv
350-
resource_info = {
357+
# Use sys.orig_argv, which has the original arguments received
358+
# by the interpreter. The spec says full command arguments are opt-in
359+
# and they can contain sensitive data.
360+
_process_command = sys.orig_argv[0] if sys.orig_argv else ""
361+
resource_info: dict[str, AttributeValue] = {
351362
PROCESS_RUNTIME_DESCRIPTION: sys.version,
352363
PROCESS_RUNTIME_NAME: sys.implementation.name,
353364
PROCESS_RUNTIME_VERSION: _runtime_version,
354365
PROCESS_PID: _process_pid,
355366
PROCESS_EXECUTABLE_NAME: _process_executable_name,
356367
PROCESS_EXECUTABLE_PATH: _process_executable_path,
357368
PROCESS_COMMAND: _process_command,
358-
PROCESS_COMMAND_LINE: _process_command_line,
359-
PROCESS_COMMAND_ARGS: _process_command_args,
360369
}
370+
if self._include_command_args:
371+
_process_argv = list(sys.orig_argv)
372+
resource_info[PROCESS_COMMAND_LINE] = " ".join(_process_argv)
373+
resource_info[PROCESS_COMMAND_ARGS] = _process_argv
374+
361375
if hasattr(os, "getppid"):
362376
# pypy3 does not have getppid()
363377
resource_info[PROCESS_PARENT_PID] = os.getppid()

opentelemetry-sdk/tests/resources/test_resources.py

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -657,6 +657,26 @@ def test_process_detector(self):
657657
self.assertEqual(
658658
aggregated_resource.attributes[PROCESS_COMMAND], sys.orig_argv[0]
659659
)
660+
self.assertNotIn(
661+
PROCESS_COMMAND_LINE,
662+
aggregated_resource.attributes,
663+
)
664+
self.assertNotIn(
665+
PROCESS_COMMAND_ARGS,
666+
aggregated_resource.attributes,
667+
)
668+
669+
@patch(
670+
"sys.orig_argv",
671+
["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"],
672+
)
673+
def test_process_detector_includes_command_args_on_opt_in(self):
674+
initial_resource = Resource({"foo": "bar"})
675+
aggregated_resource = get_aggregated_resources(
676+
[ProcessResourceDetector(include_command_args=True)],
677+
initial_resource,
678+
)
679+
660680
self.assertEqual(
661681
aggregated_resource.attributes[PROCESS_COMMAND_LINE],
662682
" ".join(sys.orig_argv),
@@ -666,6 +686,35 @@ def test_process_detector(self):
666686
tuple(sys.orig_argv),
667687
)
668688

689+
def test_process_detector_does_not_iterate_orig_argv_by_default(self):
690+
class SensitiveOrigArgv:
691+
def __getitem__(self, index):
692+
if index == 0:
693+
return "uvicorn"
694+
raise AssertionError("unexpected argv access")
695+
696+
def __iter__(self):
697+
raise AssertionError("unexpected argv iteration")
698+
699+
with patch("sys.orig_argv", SensitiveOrigArgv()):
700+
aggregated_resource = get_aggregated_resources(
701+
[ProcessResourceDetector()],
702+
Resource({"foo": "bar"}),
703+
)
704+
705+
self.assertEqual(
706+
aggregated_resource.attributes[PROCESS_COMMAND],
707+
"uvicorn",
708+
)
709+
self.assertNotIn(
710+
PROCESS_COMMAND_LINE,
711+
aggregated_resource.attributes,
712+
)
713+
self.assertNotIn(
714+
PROCESS_COMMAND_ARGS,
715+
aggregated_resource.attributes,
716+
)
717+
669718
@patch("sys.orig_argv", ["/usr/bin/python", "-m", "myapp"])
670719
def test_process_detector_uses_orig_argv_for_python_m(self):
671720
"""For ``python -m <module>`` invocations sys.argv[0] is rewritten to
@@ -677,6 +726,26 @@ def test_process_detector_uses_orig_argv_for_python_m(self):
677726
[ProcessResourceDetector()], Resource({"foo": "bar"})
678727
)
679728

729+
self.assertEqual(
730+
aggregated_resource.attributes[PROCESS_COMMAND],
731+
"/usr/bin/python",
732+
)
733+
self.assertNotIn(
734+
PROCESS_COMMAND_LINE,
735+
aggregated_resource.attributes,
736+
)
737+
self.assertNotIn(
738+
PROCESS_COMMAND_ARGS,
739+
aggregated_resource.attributes,
740+
)
741+
742+
@patch("sys.orig_argv", ["/usr/bin/python", "-m", "myapp"])
743+
def test_process_detector_uses_orig_argv_for_python_m_on_opt_in(self):
744+
aggregated_resource = get_aggregated_resources(
745+
[ProcessResourceDetector(include_command_args=True)],
746+
Resource({"foo": "bar"}),
747+
)
748+
680749
self.assertEqual(
681750
aggregated_resource.attributes[PROCESS_COMMAND],
682751
"/usr/bin/python",

0 commit comments

Comments
 (0)