Skip to content

Add validation for Trace ID #1992

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Aug 2, 2021
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `opentelemetry-distro` & `opentelemetry-sdk` Moved Auto Instrumentation Configurator code to SDK
to let distros use its default implementation
([#1937](https://github.com/open-telemetry/opentelemetry-python/pull/1937))
- Add Trace ID validation to meet [TraceID spec](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/overview.md#spancontext) ([#1992]((https://github.com/open-telemetry/opentelemetry-python/pull/1992)))

## [0.23.1](https://github.com/open-telemetry/opentelemetry-python/pull/1987) - 2021-07-26

Expand Down
26 changes: 25 additions & 1 deletion opentelemetry-api/src/opentelemetry/trace/span.py
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,26 @@ def values(self) -> typing.ValuesView[str]:

DEFAULT_TRACE_STATE = TraceState.get_default()

_TRACE_ID_BYTES = 16
_TRACE_ID_HEX_LENGTH = 2 * _TRACE_ID_BYTES


def _validate_trace_id(trace_id: int) -> bool:
"""Validates a trace_id.

Args:
trace_id: An int that is expected to corresponds to 16-bytes hexdecimal value

Returns:
True if trace_id is valid, False otherwise.
"""
if not trace_id:
return False
if len(format(trace_id, "032x")) != _TRACE_ID_HEX_LENGTH:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would this work and be a bit faster?

# constant somewhere
_MAX_TRACE_ID = (1 << 128) - 1
Suggested change
if len(format(trace_id, "032x")) != _TRACE_ID_HEX_LENGTH:
if trace_id > _MAX_TRACE_ID:

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I also prefer to make sure the trace id value is lesser than a certain value. Also, instead of (1 << 128) - 1 we can do 2 ** 128 - 1 which is subjectively easier to understand.

return False

return True
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Building off of what @aabmass said. I'm okay with constant/no constant since it's only used once but having a _MAX_TRACE_ID_LENGTH is probably easier to read!

Suggested change
if not trace_id:
return False
if len(format(trace_id, "032x")) != _TRACE_ID_HEX_LENGTH:
return False
return True
return trace_id and trace_id < (1 << 128) - 1 and trace_id != INVALID_TRACE_ID



class SpanContext(
typing.Tuple[int, int, bool, "TraceFlags", "TraceState", bool]
Expand Down Expand Up @@ -420,7 +440,11 @@ def __new__(
if trace_state is None:
trace_state = DEFAULT_TRACE_STATE

is_valid = trace_id != INVALID_TRACE_ID and span_id != INVALID_SPAN_ID
is_valid = (
trace_id != INVALID_TRACE_ID
and span_id != INVALID_SPAN_ID
Comment on lines +425 to +426
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we add move these two checks into _validate_trace_id() so it's all together?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead, we should just put the code of _validate_trace_id here instead of having a single-use function. Keep in mind that the only thing that _validate_trace_id does is trace_id < 2 ** 128.

and _validate_trace_id(trace_id)
)

return tuple.__new__(
cls,
Expand Down
9 changes: 9 additions & 0 deletions opentelemetry-api/tests/trace/test_span_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,12 @@ def test_span_context_pickle(self):
pickle_sc = pickle.loads(pickle.dumps(sc))
self.assertEqual(sc.trace_id, pickle_sc.trace_id)
self.assertEqual(sc.span_id, pickle_sc.span_id)

invalid_sc = trace.SpanContext(
9999999999999999999999999999999999999999999999999999999999999999999999999999,
9,
is_remote=False,
trace_flags=trace.DEFAULT_TRACE_OPTIONS,
trace_state=trace.DEFAULT_TRACE_STATE,
)
self.assertFalse(invalid_sc.is_valid)