Skip to content

fix/entity name validation v2#30211

Open
jaya6400 wants to merge 26 commits into
open-metadata:mainfrom
jaya6400:fix/entity-name-validation-v2
Open

fix/entity name validation v2#30211
jaya6400 wants to merge 26 commits into
open-metadata:mainfrom
jaya6400:fix/entity-name-validation-v2

Conversation

@jaya6400

@jaya6400 jaya6400 commented Jul 19, 2026

Copy link
Copy Markdown

Describe your changes:

Fixes #23268

I updated the regex patterns in JSON schema definitions to block reserved
FQN separator characters (::, >, ") and newlines (\n) from being
used in entity and column names.

The backend was accepting entity names with special characters like double
quotes and newlines, which caused issues in FQN construction since these
are reserved separator characters in OpenMetadata's FQN system.

Changes made:

  • openmetadata-spec/src/main/resources/json/schema/type/basic.json — updated pattern for entityName and testCaseEntityName
  • openmetadata-spec/src/main/resources/json/schema/entity/data/table.json — updated pattern for columnName

Pattern changed from ^((?!::).)*$ to ^((?!::)[^>\"\\x00-\\x1f])*$

Unit Test: #27521 (comment)

Type of change:

  • Bug fix

Checklist:

  • I have read the CONTRIBUTING document.
  • My PR title is Fixes #23268: <short explanation>
  • I have commented on my code, particularly in hard-to-understand areas.
  • For JSON Schema changes: I updated the migration scripts or explained why it is not needed.

Migration scripts are not needed for this change as it only tightens input validation at the schema level. Existing data is not affected — only new entity creation requests will be validated against the updated pattern.


Summary by Gitar

  • JSON Schema updates:
    • Tightened validation patterns for entityName, testCaseEntityName, columnName, and fieldName to block reserved characters ><"| and ASCII control characters.
    • Added pattern constraints to pipeline task names and searchIndex fields.
  • Backend & Model generation:
    • Updated datamodel_generation.py to remove unsupported regex patterns in generated Python models and inject custom Pydantic field_validator functions to enforce name validation.
    • Adjusted EntityCsv to properly handle validation error reporting by skipping email prefix checks when name patterns fail.
  • Testing infrastructure:
    • Added end-to-end Playwright tests for pipeline validation and updated CI configuration (playwright.config.ts) to improve test stability.
    • Synchronized TEST_CASE_NAME_REGEX in the UI with backend patterns to ensure consistent validation across the platform.
    • Standardized JVM timezone to UTC in integration tests to prevent environment-dependent failures.

This will update automatically on new commits.

Greptile Summary

This PR tightens name validation across entity schemas by expanding the existing :: negative-lookahead pattern to a character-class approach that also blocks >, ", and all ASCII control characters. The fix touches JSON schemas, the Python model-generation post-processing script, the frontend regex constants, and Java CSV import logic.

  • JSON schema changes (basic.json, table.json, searchIndex.json, pipeline.json, schema.json): Pattern updated from ^((?!::).)*$ to ^((?!::)[^>\"\\x00-\\x1f])*$; pipeline.json also gains a new minLength: 1 guard on task names.
  • Python generation (datamodel_generation.py): Pattern removal now targets all generated .py files via glob instead of a fixed list, and correctly removes both the old and new pattern variants; the previously-planned field_validator injection was dropped to avoid conflicts with transform_entity_names encoding logic.
  • Frontend / Java: ENTITY_NAME_REGEX and TEST_CASE_NAME_REGEX are unified and synced with the backend pattern; EntityCsv.shouldValidateUserNameWithEmailPrefix uses prefix matching so it works correctly regardless of the exact pattern string in the violation message.

Confidence Score: 5/5

Safe to merge — changes only tighten input validation at the schema layer with no effect on existing data, and the Python model layer correctly remains permissive for internal encoding purposes.

All changes are additive restrictions on new input only. The Java validation message matching in EntityCsv.java is correctly updated to use prefix matching rather than the old literal string. The Python model-generation script removes the unsupported pattern from all generated files, and the explanation for why no client-side validator is injected is clearly documented. Integration tests cover the key new rejection cases.

scripts/datamodel_generation.py — the six-entry patterns_to_remove list has some redundancy worth a comment, but no functional issue.

Important Files Changed

Filename Overview
openmetadata-spec/src/main/resources/json/schema/type/basic.json Updated entityName and testCaseEntityName patterns to block >, ", and ASCII control chars (\x00-\x1f) in addition to ::.
openmetadata-spec/src/main/resources/json/schema/entity/data/pipeline.json Added minLength: 1 and the new pattern constraint to the Task name field, which previously had no validation at all.
openmetadata-service/src/main/java/org/openmetadata/csv/EntityCsv.java Replaced brittle hardcoded pattern-string comparison with shouldValidateUserNameWithEmailPrefix() using startsWith prefix matching; violationList is always initialized as an empty ArrayList so no NPE risk.
scripts/datamodel_generation.py Expanded pattern removal to all generated Python files via glob; removed the now-unnecessary field_validator injection; includes 6 pattern variants to handle different code-generator escape representations. The re import is correctly removed.
openmetadata-ui/src/main/resources/ui/src/constants/regex.constants.ts Updated ENTITY_NAME_REGEX to character-class approach and aliased TEST_CASE_NAME_REGEX to it; no /g flag so sharing a reference is safe; frontend now in sync with backend patterns.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Client Request - entity name input] --> B{Frontend ENTITY_NAME_REGEX}
    B -->|fails| C[UI validation error - block submission]
    B -->|passes| D[HTTP POST to backend]
    D --> E{Java JSON Schema validation - Jakarta Validator}
    E -->|fails - name must match pattern| F[400 Bad Request]
    E -->|passes| G[Entity created or updated]

    H[CSV Import] --> I{ValidatorUtil.validate entity}
    I -->|violation starts with name must match| J[skip email-prefix check]
    I -->|no name violation| K[run email-prefix check]
    J --> L[importFailure]
    K --> M{email prefix valid?}
    M -->|no| L
    M -->|yes| N[createOrUpdate entity]

    O[Python SDK generation] --> P[Generate models from JSON Schema]
    P --> Q[Strip unsupported pattern= strings from all generated .py files]
    Q --> R[Server-side Java enforces validation - Python layer is permissive]
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    A[Client Request - entity name input] --> B{Frontend ENTITY_NAME_REGEX}
    B -->|fails| C[UI validation error - block submission]
    B -->|passes| D[HTTP POST to backend]
    D --> E{Java JSON Schema validation - Jakarta Validator}
    E -->|fails - name must match pattern| F[400 Bad Request]
    E -->|passes| G[Entity created or updated]

    H[CSV Import] --> I{ValidatorUtil.validate entity}
    I -->|violation starts with name must match| J[skip email-prefix check]
    I -->|no name violation| K[run email-prefix check]
    J --> L[importFailure]
    K --> M{email prefix valid?}
    M -->|no| L
    M -->|yes| N[createOrUpdate entity]

    O[Python SDK generation] --> P[Generate models from JSON Schema]
    P --> Q[Strip unsupported pattern= strings from all generated .py files]
    Q --> R[Server-side Java enforces validation - Python layer is permissive]
Loading

Reviews (5): Last reviewed commit: "Merge branch 'main' into fix/entity-name..." | Re-trigger Greptile

jaya6400 and others added 22 commits July 17, 2026 16:29
…names

Fixes open-metadata#23268

Updated regex patterns in JSON schema definitions to block reserved
FQN separator characters (::, >, ") and newlines in entity names.

Files changed:
- openmetadata-spec/.../type/basic.json: entityName, testCaseEntityName
- openmetadata-spec/.../entity/data/table.json: columnName
Updated pattern to use \x00-\x1f range to block all control characters
including \r, \n, \t, \0 in addition to reserved FQN characters.
This also restores the \r restriction that was present in the original
dot metacharacter but lost in the character class rewrite.
…hema fields and UI

- Added < and | to blocked characters (consistent with entityLink pattern)
- Updated searchIndexFieldName pattern in searchIndex.json
- Added pattern validation to tableConstraint columns and partitionColumnDetails in table.json
- Updated UI ENTITY_NAME_REGEX to match backend validation
- Added unit tests for new blocked characters in regex.constants.test.ts

Addresses review feedback on open-metadata#27521
Addresses inconsistency flagged in code review - columnName definition
was missing < and | from blocked characters unlike other patterns.
…erved characters

Added Java integration tests to verify that entity names containing
reserved FQN characters (::, >, <, ", |) and control characters are
rejected by the backend validation.

Files changed:
- TableResourceIT.java: added column name validation tests
- TopicResourceIT.java: added schema field name validation tests
- PipelineResourceIT.java: added task name validation tests
- SearchIndexResourceIT.java: added search index field name validation tests
- TestSuiteBootstrap.java: updated bootstrap for test suite
- pipeline.json: updated pattern for task names
- schema.json: updated pattern for schema field names
…less formatting

- Added minLength: 1 to pipeline task name to prevent empty strings
- Applied mvn spotless:apply formatting fixes
…ttern

- TEST_CASE_NAME_REGEX still used the old lookahead-only pattern and did not block `|` or ASCII control characters, unlike ENTITY_NAME_REGEX and
the tightened testCaseEntityName pattern in basic.json. This allowed
names like `my|testcase` to pass client-side validation on the data
quality test case form, then fail with a 400 from the backend.
- Reuses ENTITY_NAME_REGEX directly to prevent future drift between the two constants.
@jaya6400
jaya6400 requested review from a team as code owners July 19, 2026 10:38
@github-actions

Copy link
Copy Markdown
Contributor

❌ PR checklist incomplete

This PR cannot be merged until the following are addressed on its linked issue:

The fields live on the linked issue in the Shipping project (open the issue → right sidebar → Projects). After you set them, re-run this check (or push a commit) — issue/project changes do not re-trigger it automatically.

Maintainers can bypass this check by adding the skip-pr-checks label.

@github-actions

Copy link
Copy Markdown
Contributor

Hi there 👋 Thanks for your contribution!

The OpenMetadata team will review the PR shortly! Once it has been labeled as safe to test, the CI workflows
will start executing and we'll be able to make sure everything is working as expected.

Let us know if you need any help!

Comment thread scripts/datamodel_generation.py
Comment thread scripts/datamodel_generation.py
Comment thread scripts/datamodel_generation.py Outdated
Comment thread scripts/datamodel_generation.py Outdated
Two related issues surfaced after adding client-side name validation
for open-metadata#23268 (blocking reserved FQN characters in entity/column names):

1. scripts/datamodel_generation.py: remove the field_validator
   injection for EntityName, TestCaseEntityName, Column2, ColumnName,
   and FieldName. These validators rejected reserved characters
   (::, ><"|, control chars) at Pydantic construction time, which
   conflicts with transform_entity_names() in
   custom_basemodel_validation.py — an existing mechanism that
   deliberately allows these characters through the Python model layer
   so it can encode them (e.g. "::" -> "__reserved__colon__") on
   Create/Store models and decode them back on Fetch models.
   Constructing the raw-named object is a prerequisite for that
   encode step, so rejecting at construction broke it entirely
   (test_custom_basemodel_validation.py).

2. openmetadata-spec/.../entity/data/table.json: revert the inline
   "pattern" added to tableConstraint.columns items and
   partitionColumnDetails.columnName. Adding a pattern there forced
   datamodel-code-generator to wrap those fields in a dedicated
   RootModel class instead of plain str, changing
   TableConstraint.columns from List[str] to List[Column2]. That broke
   every call site treating constraint columns as plain strings, e.g.
   normalize_table_constraints()'s `c.lower()` in database_service.py,
   causing failures across test_common_db_source, test_bigquery,
   test_burstiq_metadata, test_filter_invalid_constraints,
   test_cockroach, test_table_constraints, and test_custom_pydantic.

In both cases the actual enforcement of open-metadata#23268 is unaffected: the
JSON Schema pattern on the columnName/entityName/testCaseEntityName
definitions themselves (validated server-side) and the UI's
ENTITY_NAME_REGEX are untouched. A constraint's columns list only
references names that were already validated when the column itself
was created, so re-validating (and re-typing) it a second time was
redundant and is what caused the regressions.

Verified: full ingestion unit-test suite passes (124 passed).
@github-actions

Copy link
Copy Markdown
Contributor

Hi there 👋 Thanks for your contribution!

The OpenMetadata team will review the PR shortly! Once it has been labeled as safe to test, the CI workflows
will start executing and we'll be able to make sure everything is working as expected.

Let us know if you need any help!

@gitar-bot

gitar-bot Bot commented Jul 19, 2026

Copy link
Copy Markdown
Code Review ✅ Approved 2 resolved / 2 findings

Tightens entity and column name validation across the platform to block reserved characters and control codes, resolving issues with FQN construction. This update includes automated codegen fixes for Pydantic models, UI constant synchronization, and improved E2E test coverage.

✅ 2 resolved
Quality: Codegen validator injection silently no-ops if output changes

📄 scripts/datamodel_generation.py:81-87 📄 scripts/datamodel_generation.py:143-153 📄 scripts/datamodel_generation.py:181-195 📄 scripts/datamodel_generation.py:246-260
The pattern removal and custom-validator injection all rely on matching exact literal text emitted by datamodel-code-generator (class names like Column2, precise import lines, and full multi-line class ...(RootModel[str]) bodies). If the generator changes numbering, field order, or formatting, str.replace/replace_class silently match nothing and no error is raised — the generated models would ship with the regex pattern stripped but no replacement validator, silently dropping all client-side entity-name validation. Add a post-condition assertion (e.g. verify each expected validator string is present in the final content, else raise) so a future regeneration fails loudly instead of quietly losing validation.

Edge Case: Client-side validation dropped for task/field/constraint column names

📄 scripts/datamodel_generation.py:72-86 📄 openmetadata-spec/src/main/resources/json/schema/entity/data/searchIndex.json:100 📄 openmetadata-spec/src/main/resources/json/schema/entity/data/pipeline.json:110-112 📄 openmetadata-spec/src/main/resources/json/schema/entity/data/table.json:212 📄 openmetadata-spec/src/main/resources/json/schema/entity/data/table.json:278
patterns_to_remove strips the new pattern from every generated file, but custom Pydantic validators are only re-added for EntityName, TestCaseEntityName, ColumnName, Column2, and FieldName. Inline/typed patterns such as searchIndexFieldName, pipeline task name, and the table constraint/partition columnName fields lose their pattern in the ingestion models with no replacement validator, so those names are no longer validated client-side (only the server rejects them). This is consistent with the stated "let the server validate" approach but is inconsistent with the classes that did get validators; consider either covering these fields too or documenting the intentional gap.

Options

Display: compact → Showing less information.

Comment with these commands to change the behavior for this request:

Compact
gitar display:verbose         

Was this helpful? React with 👍 / 👎 | Gitar

@github-actions

Copy link
Copy Markdown
Contributor

Jest test Coverage

UI tests summary

Lines Statements Branches Functions
Coverage: 64%
65.02% (75532/116167) 48.85% (45036/92186) 49.62% (13617/27441)

@sonarqubecloud

Copy link
Copy Markdown

@sonarqubecloud

Copy link
Copy Markdown

@github-actions

github-actions Bot commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

🔴 Playwright Results — 4 pipeline/setup failure(s)

✅ 0 passed · ❌ 0 failed · 🟡 0 flaky · ⏭️ 0 skipped

Pipeline and setup failures

  • The build job finished with status failure.
  • Shard detection finished with status failure.
  • The Playwright shard matrix was unexpectedly skipped.
  • No expected Playwright shards were declared.
Shard Passed Failed Flaky Skipped

📦 Download artifacts

How to debug locally
# Download playwright-test-results-<shard> artifact and unzip
npx playwright show-trace path/to/trace.zip    # view trace

@jaya6400

Copy link
Copy Markdown
Author

Hi @chirag-madlani could you check again, most playwright tests are passing now. could we merge it?

Thanks @IceS2 for adding the safe to test label and running the workflows.

@IceS2

IceS2 commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Hey @jaya6400, This is looking great but there is still one thing I had flagged I would like you to check out:

  • The pattern blocks < and |, but ingestion has no way to encode those

Replace_separators in custom_basemodel_validation.py only handles ::, >, ", \n, \r, \t:

  value.replace("::", RESERVED_COLON_KEYWORD)
       .replace(">",  RESERVED_ARROW_KEYWORD)
       .replace('"',  RESERVED_QUOTE_KEYWORD)
       .replace("\n", RESERVED_NEWLINE_KEYWORD)
       .replace("\r", RESERVED_CARRIAGE_RETURN_KEYWORD)
       .replace("\t", RESERVED_TAB_KEYWORD)

That means that they would reach the server raw and fail. I think the simplest way is dropping them from the pattern since they should not cause any issues, what do you think?

@jaya6400

Copy link
Copy Markdown
Author

Thanks @IceS2 for your feedback (#30211 (comment)), now < and | are intentionally allowed and removed from the pattern regex based on the concerns with custom_basemodel_validation.py u raised.

  • Also, updated the Java and Typescript tests files accordingly.

Could u review and run the workflows again?

Latest commits: c5afe0c , 7b70b67

@gitar-bot

gitar-bot Bot commented Jul 20, 2026

Copy link
Copy Markdown
Code Review ✅ Approved 2 resolved / 2 findings

Tightens entity and column name validation to block reserved FQN characters, ensuring consistency across backend schemas, frontend regex, and generated models. All identified validation regressions and test coverage gaps have been successfully addressed.

✅ 2 resolved
Quality: Codegen validator injection silently no-ops if output changes

📄 scripts/datamodel_generation.py:81-87 📄 scripts/datamodel_generation.py:143-153 📄 scripts/datamodel_generation.py:181-195 📄 scripts/datamodel_generation.py:246-260
The pattern removal and custom-validator injection all rely on matching exact literal text emitted by datamodel-code-generator (class names like Column2, precise import lines, and full multi-line class ...(RootModel[str]) bodies). If the generator changes numbering, field order, or formatting, str.replace/replace_class silently match nothing and no error is raised — the generated models would ship with the regex pattern stripped but no replacement validator, silently dropping all client-side entity-name validation. Add a post-condition assertion (e.g. verify each expected validator string is present in the final content, else raise) so a future regeneration fails loudly instead of quietly losing validation.

Edge Case: Client-side validation dropped for task/field/constraint column names

📄 scripts/datamodel_generation.py:72-86 📄 openmetadata-spec/src/main/resources/json/schema/entity/data/searchIndex.json:100 📄 openmetadata-spec/src/main/resources/json/schema/entity/data/pipeline.json:110-112 📄 openmetadata-spec/src/main/resources/json/schema/entity/data/table.json:212 📄 openmetadata-spec/src/main/resources/json/schema/entity/data/table.json:278
patterns_to_remove strips the new pattern from every generated file, but custom Pydantic validators are only re-added for EntityName, TestCaseEntityName, ColumnName, Column2, and FieldName. Inline/typed patterns such as searchIndexFieldName, pipeline task name, and the table constraint/partition columnName fields lose their pattern in the ingestion models with no replacement validator, so those names are no longer validated client-side (only the server rejects them). This is consistent with the stated "let the server validate" approach but is inconsistent with the classes that did get validators; consider either covering these fields too or documenting the intentional gap.

Options

Display: compact → Showing less information.

Comment with these commands to change the behavior for this request:

Compact
gitar display:verbose         

Was this helpful? React with 👍 / 👎 | Gitar

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

Labels

safe to test Add this label to run secure Github workflows on PRs

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Backend: Validate if the entity names are valid in terms of Name, Fqn

2 participants