Skip to content

Enforce schema character set on vital.name in Feature Operation APIs#2864

Open
Valpertui wants to merge 2 commits into
developfrom
valpertui/rum-operation-name-validation
Open

Enforce schema character set on vital.name in Feature Operation APIs#2864
Valpertui wants to merge 2 commits into
developfrom
valpertui/rum-operation-name-validation

Conversation

@Valpertui
Copy link
Copy Markdown
Member

What and why?

The authoritative _vital-common-schema.json restricts vital.name to the character set [\w.@$-]* (letters, digits, _, ., @, $, -) and the backend enforces this as a server-side regex on the facet path. Until now the iOS SDK only rejected blank names; non-conforming values (e.g. "user login", "api/v1", "ログイン") were silently accepted and shipped to intake.

This PR validates the character set at the API boundary so developers catch the problem at dev time instead of discovering it only after events are dropped at intake.

How?

Split RUMFeatureOperationManager's single validateString helper into:

  • validateName — rejects blank names with DD.logger.error (drops the event), then checks each unicode scalar against an explicit ASCII CharacterSet(charactersIn: "A-Za-z0-9_.@$-"). Using .alphanumerics would have been wrong — it is Unicode-aware and would accept Japanese/Cyrillic/etc.
  • validateOperationKey — blank-check only (schema does not restrict operation_key).

When name contains characters outside the allowed set, DD.logger.warn is emitted quoting the exact backend pattern [\w.@$-]*, but the event is still emitted. The rationale: the backend is the source of truth on the policy, so a client-side drop would force customers to bump the SDK if the server rule is ever relaxed.

Updated RUMFeatureMocks.mockRandom() to use .alphanumerics so existing forgery-based tests stay within the allowed set.

Tests cover:

  • blank / whitespace-only name → dropped + error
  • invalid ASCII chars (space, slash, colon, comma, plus, tab) → warn + emitted
  • non-ASCII name "ログイン" and emoji "login🔐" → warn + emitted (pins ASCII-only semantics against future .alphanumerics regressions)
  • every allowed-character class → silent emit
  • operationKey with space/slash/etc. → silent emit (no char-set rule)
  • applies to all three methods: start/succeed/fail

Review checklist

  • Feature or bugfix MUST have appropriate tests (unit, integration)
  • Make sure each commit and the PR mention the Issue number or JIRA reference
  • Add CHANGELOG entry for user facing changes
  • Add Objective-C interface for public APIs — not applicable, change is internal
  • Run make api-surface when adding new APIs — not applicable, no new public API

The backend enforces [\w.@$-]* on vital.name via the vital-common-schema
facet-path rule; until now the iOS SDK only checked for blank names.
This change validates the character set in RUMFeatureOperationManager:
blank names are dropped with an error, names that fail the regex warn
but are still emitted (backend is source of truth on the policy).

operation_key has no schema character-set rule and is left untouched.
mockRandom() on RUMOperationStepVitalCommand now generates ASCII
alphanumeric names so existing forgery-based tests stay within the
allowed set.
@Valpertui Valpertui requested review from a team as code owners April 23, 2026 18:07
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5d7f226b0a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".


guard !trimmed.isEmpty else {
DD.logger.error("Operation `operationKey`, when provided, cannot be empty or contain only whitespace/line breaks. \(stepType) command will be ignored.")
return false
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Shouldn't the validation of the operationKey be relaxed? It is an optional parameter, so it could also be ignored if empty. This way the operation step would not be discarded completely.


// Then — every event is emitted; the name is preserved verbatim.
let vitalEvents = mockWriter.events(ofType: RUMVitalOperationStepEvent.self)
XCTAssertEqual(vitalEvents.count, invalidCharacterSetNames.count)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Can you validate the warning messages being logged with DD.mockWith(logger: CoreLoggerMock())?


// Then
let vitalEvents = mockWriter.events(ofType: RUMVitalOperationStepEvent.self)
XCTAssertEqual(vitalEvents.count, validNames.count)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

And here no warnings in the dd.logger.warnLog

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants