From 1e493d91b059b693d82c59f09c2ae91be9c99b18 Mon Sep 17 00:00:00 2001 From: JAYA DUBEY Date: Sun, 19 Apr 2026 14:14:13 +0530 Subject: [PATCH 01/21] fix(validation): reject reserved FQN characters in entity and column names Fixes #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 --- .../src/main/resources/json/schema/entity/data/table.json | 2 +- .../src/main/resources/json/schema/type/basic.json | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/openmetadata-spec/src/main/resources/json/schema/entity/data/table.json b/openmetadata-spec/src/main/resources/json/schema/entity/data/table.json index c39ee7b56ba4..b194bb8ecfbe 100644 --- a/openmetadata-spec/src/main/resources/json/schema/entity/data/table.json +++ b/openmetadata-spec/src/main/resources/json/schema/entity/data/table.json @@ -235,7 +235,7 @@ "description": "Local name (not fully qualified name) of the column. ColumnName is `-` when the column is not named in struct dataType. For example, BigQuery supports struct with unnamed fields.", "type": "string", "minLength": 1, - "pattern": "^((?!::).)*$" + "pattern": "^((?!::)[^>\"\n])*$" }, "partitionIntervalTypes": { "javaType": "org.openmetadata.schema.type.PartitionIntervalTypes", diff --git a/openmetadata-spec/src/main/resources/json/schema/type/basic.json b/openmetadata-spec/src/main/resources/json/schema/type/basic.json index 37b6e0aa8f8f..8662f56aa443 100644 --- a/openmetadata-spec/src/main/resources/json/schema/type/basic.json +++ b/openmetadata-spec/src/main/resources/json/schema/type/basic.json @@ -124,13 +124,13 @@ "type": "string", "minLength": 1, "maxLength": 256, - "pattern": "^((?!::).)*$" + "pattern": "^((?!::)[^>\"\n])*$" }, "testCaseEntityName": { "description": "Name that identifies a test definition and test case.", "type": "string", "minLength": 1, - "pattern": "^((?!::).)*$" + "pattern": "^((?!::)[^>\"\n])*$" }, "fullyQualifiedEntityName": { "description": "A unique name that identifies an entity. Example for table 'DatabaseService.Database.Schema.Table'.", From 7544edca8b98cc74d7abe53ab7953c2cf7d7fba6 Mon Sep 17 00:00:00 2001 From: JAYA DUBEY Date: Sun, 19 Apr 2026 14:31:47 +0530 Subject: [PATCH 02/21] fix(validation): also block control characters in entity name pattern 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. --- .../src/main/resources/json/schema/entity/data/table.json | 2 +- .../src/main/resources/json/schema/type/basic.json | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/openmetadata-spec/src/main/resources/json/schema/entity/data/table.json b/openmetadata-spec/src/main/resources/json/schema/entity/data/table.json index b194bb8ecfbe..85d86ede390e 100644 --- a/openmetadata-spec/src/main/resources/json/schema/entity/data/table.json +++ b/openmetadata-spec/src/main/resources/json/schema/entity/data/table.json @@ -235,7 +235,7 @@ "description": "Local name (not fully qualified name) of the column. ColumnName is `-` when the column is not named in struct dataType. For example, BigQuery supports struct with unnamed fields.", "type": "string", "minLength": 1, - "pattern": "^((?!::)[^>\"\n])*$" + "pattern": "^((?!::)[^>\"\\x00-\\x1f])*$" }, "partitionIntervalTypes": { "javaType": "org.openmetadata.schema.type.PartitionIntervalTypes", diff --git a/openmetadata-spec/src/main/resources/json/schema/type/basic.json b/openmetadata-spec/src/main/resources/json/schema/type/basic.json index 8662f56aa443..2b725e7e9da8 100644 --- a/openmetadata-spec/src/main/resources/json/schema/type/basic.json +++ b/openmetadata-spec/src/main/resources/json/schema/type/basic.json @@ -124,13 +124,13 @@ "type": "string", "minLength": 1, "maxLength": 256, - "pattern": "^((?!::)[^>\"\n])*$" + "pattern": "^((?!::)[^>\"\\x00-\\x1f])*$" }, "testCaseEntityName": { "description": "Name that identifies a test definition and test case.", "type": "string", "minLength": 1, - "pattern": "^((?!::)[^>\"\n])*$" + "pattern": "^((?!::)[^>\"\\x00-\\x1f])*$" }, "fullyQualifiedEntityName": { "description": "A unique name that identifies an entity. Example for table 'DatabaseService.Database.Schema.Table'.", From a0b7a4a553ed9619e4950420e842b6c43f1ecbcf Mon Sep 17 00:00:00 2001 From: JAYA DUBEY Date: Wed, 22 Apr 2026 16:17:38 +0530 Subject: [PATCH 03/21] fix(validation): extend entity name validation to cover additional schema 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 #27521 --- .../main/resources/json/schema/entity/data/searchIndex.json | 2 +- .../src/main/resources/json/schema/entity/data/table.json | 6 ++++-- .../src/main/resources/json/schema/type/basic.json | 4 ++-- .../main/resources/ui/src/constants/regex.constants.test.ts | 6 ++++++ .../src/main/resources/ui/src/constants/regex.constants.ts | 2 +- 5 files changed, 14 insertions(+), 6 deletions(-) diff --git a/openmetadata-spec/src/main/resources/json/schema/entity/data/searchIndex.json b/openmetadata-spec/src/main/resources/json/schema/entity/data/searchIndex.json index d35d2e74224c..6ebcd116b538 100644 --- a/openmetadata-spec/src/main/resources/json/schema/entity/data/searchIndex.json +++ b/openmetadata-spec/src/main/resources/json/schema/entity/data/searchIndex.json @@ -97,7 +97,7 @@ "type": "string", "minLength": 1, "maxLength": 256, - "pattern": "^((?!::).)*$" + "pattern": "^((?!::)[^><\"|\\x00-\\x1f])*$" }, "searchIndexField": { "type": "object", diff --git a/openmetadata-spec/src/main/resources/json/schema/entity/data/table.json b/openmetadata-spec/src/main/resources/json/schema/entity/data/table.json index 85d86ede390e..70dd9eaff583 100644 --- a/openmetadata-spec/src/main/resources/json/schema/entity/data/table.json +++ b/openmetadata-spec/src/main/resources/json/schema/entity/data/table.json @@ -208,7 +208,8 @@ "description": "List of column names corresponding to the constraint.", "type": "array", "items": { - "type": "string" + "type": "string", + "pattern": "^((?!::)[^><\"|\\x00-\\x1f])*$" } }, "referredColumns": { @@ -273,7 +274,8 @@ "properties": { "columnName": { "description": "List of column names corresponding to the partition.", - "type": "string" + "type": "string", + "pattern": "^((?!::)[^><\"|\\x00-\\x1f])*$" }, "intervalType": { "$ref": "#/definitions/partitionIntervalTypes" diff --git a/openmetadata-spec/src/main/resources/json/schema/type/basic.json b/openmetadata-spec/src/main/resources/json/schema/type/basic.json index 2b725e7e9da8..22996b882adb 100644 --- a/openmetadata-spec/src/main/resources/json/schema/type/basic.json +++ b/openmetadata-spec/src/main/resources/json/schema/type/basic.json @@ -124,13 +124,13 @@ "type": "string", "minLength": 1, "maxLength": 256, - "pattern": "^((?!::)[^>\"\\x00-\\x1f])*$" + "pattern": "^((?!::)[^><\"|\\x00-\\x1f])*$" }, "testCaseEntityName": { "description": "Name that identifies a test definition and test case.", "type": "string", "minLength": 1, - "pattern": "^((?!::)[^>\"\\x00-\\x1f])*$" + "pattern": "^((?!::)[^><\"|\\x00-\\x1f])*$" }, "fullyQualifiedEntityName": { "description": "A unique name that identifies an entity. Example for table 'DatabaseService.Database.Schema.Table'.", diff --git a/openmetadata-ui/src/main/resources/ui/src/constants/regex.constants.test.ts b/openmetadata-ui/src/main/resources/ui/src/constants/regex.constants.test.ts index f37650cc914f..e47e5e75fa8e 100644 --- a/openmetadata-ui/src/main/resources/ui/src/constants/regex.constants.test.ts +++ b/openmetadata-ui/src/main/resources/ui/src/constants/regex.constants.test.ts @@ -162,6 +162,12 @@ describe('Test Regex', () => { it('EntityName regex should fail for the invalid entity name', () => { // conatines :: in the name should fail expect(ENTITY_NAME_REGEX.test('Hello::World')).toEqual(false); + expect(ENTITY_NAME_REGEX.test('name>bad')).toEqual(false); + expect(ENTITY_NAME_REGEX.test('name { diff --git a/openmetadata-ui/src/main/resources/ui/src/constants/regex.constants.ts b/openmetadata-ui/src/main/resources/ui/src/constants/regex.constants.ts index be9ee2e167f7..46d41e389088 100644 --- a/openmetadata-ui/src/main/resources/ui/src/constants/regex.constants.ts +++ b/openmetadata-ui/src/main/resources/ui/src/constants/regex.constants.ts @@ -20,7 +20,7 @@ export const EMAIL_REG_EX = /^\S+@\S+\.\S+$/; * strings that contain a combination of letters, alphanumeric characters, hyphens, * spaces, periods, single quotes, ampersands, and parentheses, with support for Unicode characters. */ -export const ENTITY_NAME_REGEX = /^((?!::).)*$/; +export const ENTITY_NAME_REGEX = /^((?!::)[^><"|\u0000-\u001f])*$/; /** * Matches any string that does NOT contain the following: From d2e375d940768fc4142ee3526011016ad995409a Mon Sep 17 00:00:00 2001 From: JAYA DUBEY Date: Wed, 22 Apr 2026 16:36:37 +0530 Subject: [PATCH 04/21] fix(validation): add < and | to columnName pattern in table.json Addresses inconsistency flagged in code review - columnName definition was missing < and | from blocked characters unlike other patterns. --- .../src/main/resources/json/schema/entity/data/table.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openmetadata-spec/src/main/resources/json/schema/entity/data/table.json b/openmetadata-spec/src/main/resources/json/schema/entity/data/table.json index 70dd9eaff583..c6754c0b3cf7 100644 --- a/openmetadata-spec/src/main/resources/json/schema/entity/data/table.json +++ b/openmetadata-spec/src/main/resources/json/schema/entity/data/table.json @@ -236,7 +236,7 @@ "description": "Local name (not fully qualified name) of the column. ColumnName is `-` when the column is not named in struct dataType. For example, BigQuery supports struct with unnamed fields.", "type": "string", "minLength": 1, - "pattern": "^((?!::)[^>\"\\x00-\\x1f])*$" + "pattern": "^((?!::)[^><\"|\\x00-\\x1f])*$" }, "partitionIntervalTypes": { "javaType": "org.openmetadata.schema.type.PartitionIntervalTypes", From 69bbdb6c28076ce33db4ede699d0fe92d315f982 Mon Sep 17 00:00:00 2001 From: JAYA DUBEY Date: Wed, 22 Apr 2026 18:03:15 +0530 Subject: [PATCH 05/21] fix(lint): disable no-control-regex for intentional control char range in ENTITY_NAME_REGEX --- .../src/main/resources/ui/src/constants/regex.constants.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/openmetadata-ui/src/main/resources/ui/src/constants/regex.constants.ts b/openmetadata-ui/src/main/resources/ui/src/constants/regex.constants.ts index 46d41e389088..3e773aa260e5 100644 --- a/openmetadata-ui/src/main/resources/ui/src/constants/regex.constants.ts +++ b/openmetadata-ui/src/main/resources/ui/src/constants/regex.constants.ts @@ -17,9 +17,10 @@ export const UrlEntityCharRegEx = /[#.%;?/\\]/g; export const EMAIL_REG_EX = /^\S+@\S+\.\S+$/; /** - * strings that contain a combination of letters, alphanumeric characters, hyphens, - * spaces, periods, single quotes, ampersands, and parentheses, with support for Unicode characters. + * Validates entity names. Blocks reserved FQN separator characters (::, >, <, ", |) + * and ASCII control characters. Supports Unicode characters. */ +// eslint-disable-next-line no-control-regex export const ENTITY_NAME_REGEX = /^((?!::)[^><"|\u0000-\u001f])*$/; /** From d7e09e33c52bbb03cc61f08aeefb7d5b92b456e0 Mon Sep 17 00:00:00 2001 From: JAYA DUBEY Date: Sat, 25 Apr 2026 21:54:08 +0530 Subject: [PATCH 06/21] fix(tests): add integration tests for entity name validation with reserved 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 --- .../it/bootstrap/TestSuiteBootstrap.java | 11 +++++-- .../it/tests/PipelineResourceIT.java | 15 +++++++++ .../it/tests/SearchIndexResourceIT.java | 16 ++++++++++ .../it/tests/TableResourceIT.java | 32 +++++++++++++++++++ .../it/tests/TopicResourceIT.java | 22 +++++++++++++ .../json/schema/entity/data/pipeline.json | 3 +- .../resources/json/schema/type/schema.json | 5 +-- 7 files changed, 99 insertions(+), 5 deletions(-) diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/bootstrap/TestSuiteBootstrap.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/bootstrap/TestSuiteBootstrap.java index e5507bee3ad0..a62a58aa9234 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/bootstrap/TestSuiteBootstrap.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/bootstrap/TestSuiteBootstrap.java @@ -28,8 +28,11 @@ import jakarta.validation.Validator; import java.io.IOException; import java.lang.reflect.Field; +import java.nio.file.Path; +import java.nio.file.Paths; import java.time.Duration; import java.util.Map; +import java.util.TimeZone; import java.util.concurrent.atomic.AtomicBoolean; import org.apache.hc.client5.http.auth.AuthScope; import org.apache.hc.client5.http.auth.UsernamePasswordCredentials; @@ -162,6 +165,9 @@ public void launcherSessionOpened(LauncherSession session) { cacheProvider = System.getProperty("cacheProvider", "none"); LOG.info("=== TestSuiteBootstrap: Starting test infrastructure ==="); + System.setProperty("user.timezone", "UTC"); + TimeZone.setDefault(TimeZone.getTimeZone("UTC")); + LOG.info("Test JVM timezone set to {}", TimeZone.getDefault().getID()); LOG.info("Database type: {}", databaseType); LOG.info("Search type: {}", searchType); LOG.info("RDF enabled: {}", rdfEnabled); @@ -505,8 +511,9 @@ private void startApplication() throws Exception { config.setDataSourceFactory(dataSourceFactory); String projectRoot = System.getProperty("user.dir"); - if (projectRoot.endsWith("openmetadata-integration-tests")) { - projectRoot = projectRoot.substring(0, projectRoot.lastIndexOf("/")); + Path projectRootPath = Paths.get(projectRoot); + if (projectRootPath.endsWith("openmetadata-integration-tests") && projectRootPath.getParent() != null) { + projectRoot = projectRootPath.getParent().toString(); } String flyWayMigrationScriptsLocation = projectRoot + "/bootstrap/sql/migrations/flyway/" + DATABASE_CONTAINER.getDriverClassName(); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/PipelineResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/PipelineResourceIT.java index 0e713ea15ad9..cecec74ab379 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/PipelineResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/PipelineResourceIT.java @@ -213,6 +213,21 @@ void post_pipelineWithTasks_200_OK(TestNamespace ns) { assertEquals(2, pipeline.getTasks().size()); } + @Test + void post_pipelineWithInvalidTaskName_4xx(TestNamespace ns) { + PipelineService service = PipelineServiceTestFactory.createAirflow(ns); + + CreatePipeline request = new CreatePipeline(); + request.setName(ns.prefix("pipeline_invalid_task")); + request.setService(service.getFullyQualifiedName()); + request.setTasks(List.of(new Task().withName("task createEntity(request), + "Creating pipeline with invalid task name should fail"); + } + @Test void post_pipelineWithSourceUrl_200_OK(TestNamespace ns) { OpenMetadataClient client = SdkClients.adminClient(); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/SearchIndexResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/SearchIndexResourceIT.java index 5903ff1fad82..0595d2c7b9bc 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/SearchIndexResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/SearchIndexResourceIT.java @@ -234,6 +234,22 @@ void post_searchIndexWithFields_200_OK(TestNamespace ns) { assertEquals(2, searchIndex.getFields().size()); } + @Test + void post_searchIndexWithInvalidFieldName_4xx(TestNamespace ns) { + SearchService service = SearchServiceTestFactory.createElasticSearch(ns); + + CreateSearchIndex request = new CreateSearchIndex(); + request.setName(ns.prefix("searchindex_invalid_field")); + request.setService(service.getFullyQualifiedName()); + request.setFields( + List.of(new SearchIndexField().withName("title createEntity(request), + "Creating search index with invalid field name should fail"); + } + @Test void put_searchIndexFields_200_OK(TestNamespace ns) { OpenMetadataClient client = SdkClients.adminClient(); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/TableResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/TableResourceIT.java index 294bd3926f94..4da0d410be7a 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/TableResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/TableResourceIT.java @@ -220,6 +220,38 @@ protected Table createEntity(CreateTable createRequest) { return SdkClients.adminClient().tables().create(createRequest); } + @Test + void post_tableWithInvalidConstraintOrPartitionColumnName_4xx(TestNamespace ns) { + CreateTable invalidConstraintRequest = createMinimalRequest(ns); + invalidConstraintRequest.setName(ns.prefix("table_invalid_constraint_column")); + invalidConstraintRequest.setTableConstraints( + List.of( + new TableConstraint() + .withConstraintType(TableConstraint.ConstraintType.UNIQUE) + .withColumns(List.of("name createEntity(invalidConstraintRequest), + "Creating table with invalid constraint column name should fail"); + + CreateTable invalidPartitionRequest = createMinimalRequest(ns); + invalidPartitionRequest.setName(ns.prefix("table_invalid_partition_column")); + invalidPartitionRequest.setTablePartition( + new TablePartition() + .withColumns( + List.of( + new PartitionColumnDetails() + .withColumnName("name|invalid") + .withIntervalType(PartitionIntervalTypes.COLUMN_VALUE) + .withInterval("daily")))); + + assertThrows( + Exception.class, + () -> createEntity(invalidPartitionRequest), + "Creating table with invalid partition column name should fail"); + } + @Override protected Table getEntity(String id) { return SdkClients.adminClient().tables().get(id); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/TopicResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/TopicResourceIT.java index b4c1b8ee13e4..d3f6b7780fe9 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/TopicResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/TopicResourceIT.java @@ -247,6 +247,28 @@ void post_topicWithMessageSchema_200_OK(TestNamespace ns) { assertNotNull(topic.getMessageSchema().getSchemaFields()); } + @Test + void post_topicWithInvalidSchemaFieldName_4xx(TestNamespace ns) { + MessagingService service = MessagingServiceTestFactory.createKafka(ns); + + MessageSchema schema = + new MessageSchema() + .withSchemaType(SchemaType.JSON) + .withSchemaFields( + List.of(new Field().withName("field|invalid").withDataType(FieldDataType.STRING))); + + CreateTopic request = new CreateTopic(); + request.setName(ns.prefix("topic_invalid_schema_field")); + request.setService(service.getFullyQualifiedName()); + request.setPartitions(1); + request.setMessageSchema(schema); + + assertThrows( + Exception.class, + () -> createEntity(request), + "Creating topic with invalid schema field name should fail"); + } + @Test void post_topicWithCleanupPolicies_200_OK(TestNamespace ns) { OpenMetadataClient client = SdkClients.adminClient(); diff --git a/openmetadata-spec/src/main/resources/json/schema/entity/data/pipeline.json b/openmetadata-spec/src/main/resources/json/schema/entity/data/pipeline.json index 67106e460959..c26b8a993677 100644 --- a/openmetadata-spec/src/main/resources/json/schema/entity/data/pipeline.json +++ b/openmetadata-spec/src/main/resources/json/schema/entity/data/pipeline.json @@ -107,7 +107,8 @@ "properties": { "name": { "description": "Name that identifies this task instance uniquely.", - "type": "string" + "type": "string", + "pattern": "^((?!::)[^><\"|\\x00-\\x1f])*$" }, "displayName": { "description": "Display Name that identifies this Task. It could be title or label from the pipeline services.", diff --git a/openmetadata-spec/src/main/resources/json/schema/type/schema.json b/openmetadata-spec/src/main/resources/json/schema/type/schema.json index 6d67da26a9e7..06fde346fbe6 100644 --- a/openmetadata-spec/src/main/resources/json/schema/type/schema.json +++ b/openmetadata-spec/src/main/resources/json/schema/type/schema.json @@ -66,7 +66,8 @@ "description": "Local name (not fully qualified name) of the field. ", "type": "string", "minLength": 1, - "maxLength": 128 + "maxLength": 128, + "pattern": "^((?!::)[^><\"|\\x00-\\x1f])*$" }, "field": { "type": "object", @@ -139,4 +140,4 @@ "default": [] } } -} \ No newline at end of file +} From bf068460b861e30663d6f038eed01c06300c1922 Mon Sep 17 00:00:00 2001 From: JAYA DUBEY Date: Sat, 25 Apr 2026 22:05:34 +0530 Subject: [PATCH 07/21] fix(validation): add minLength:1 to pipeline task name and apply spotless formatting - Added minLength: 1 to pipeline task name to prevent empty strings - Applied mvn spotless:apply formatting fixes --- .../org/openmetadata/it/bootstrap/TestSuiteBootstrap.java | 3 ++- .../org/openmetadata/it/tests/SearchIndexResourceIT.java | 5 ++++- .../src/main/resources/json/schema/entity/data/pipeline.json | 1 + 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/bootstrap/TestSuiteBootstrap.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/bootstrap/TestSuiteBootstrap.java index a62a58aa9234..63cc226de6a4 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/bootstrap/TestSuiteBootstrap.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/bootstrap/TestSuiteBootstrap.java @@ -512,7 +512,8 @@ private void startApplication() throws Exception { String projectRoot = System.getProperty("user.dir"); Path projectRootPath = Paths.get(projectRoot); - if (projectRootPath.endsWith("openmetadata-integration-tests") && projectRootPath.getParent() != null) { + if (projectRootPath.endsWith("openmetadata-integration-tests") + && projectRootPath.getParent() != null) { projectRoot = projectRootPath.getParent().toString(); } String flyWayMigrationScriptsLocation = diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/SearchIndexResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/SearchIndexResourceIT.java index 0595d2c7b9bc..4ac53f45b216 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/SearchIndexResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/SearchIndexResourceIT.java @@ -242,7 +242,10 @@ void post_searchIndexWithInvalidFieldName_4xx(TestNamespace ns) { request.setName(ns.prefix("searchindex_invalid_field")); request.setService(service.getFullyQualifiedName()); request.setFields( - List.of(new SearchIndexField().withName("title<\"|\\x00-\\x1f])*$" }, "displayName": { From ae93a76130ed2db5f396d28f02f45f067d02ee11 Mon Sep 17 00:00:00 2001 From: JAYA DUBEY Date: Tue, 28 Apr 2026 20:16:12 +0530 Subject: [PATCH 08/21] Add validation tests for empty pipeline task names - Add integration test in PipelineResourceIT.java to validate that creating a pipeline with empty task name returns 4xx error - Add Playwright E2E test in PipelineValidation.spec.ts to validate empty task name rejection via API - Tests verify the existing JSON schema validation (minLength: 1 on task name field) works correctly --- .../it/tests/PipelineResourceIT.java | 15 ++++ .../e2e/Pages/PipelineValidation.spec.ts | 77 +++++++++++++++++++ 2 files changed, 92 insertions(+) create mode 100644 openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/PipelineValidation.spec.ts diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/PipelineResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/PipelineResourceIT.java index cecec74ab379..7e6443b47f48 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/PipelineResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/PipelineResourceIT.java @@ -228,6 +228,21 @@ void post_pipelineWithInvalidTaskName_4xx(TestNamespace ns) { "Creating pipeline with invalid task name should fail"); } + @Test + void post_pipelineWithEmptyTaskName_4xx(TestNamespace ns) { + PipelineService service = PipelineServiceTestFactory.createAirflow(ns); + + CreatePipeline request = new CreatePipeline(); + request.setName(ns.prefix("pipeline_empty_task_name")); + request.setService(service.getFullyQualifiedName()); + request.setTasks(List.of(new Task().withName(""))); + + assertThrows( + Exception.class, + () -> createEntity(request), + "Creating pipeline with empty task name should fail"); + } + @Test void post_pipelineWithSourceUrl_200_OK(TestNamespace ns) { OpenMetadataClient client = SdkClients.adminClient(); diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/PipelineValidation.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/PipelineValidation.spec.ts new file mode 100644 index 000000000000..2c28f0527e2e --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/PipelineValidation.spec.ts @@ -0,0 +1,77 @@ +/* + * Copyright 2026 Collate. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { expect, test, type APIRequestContext } from '@playwright/test'; +import { getDefaultAdminAPIContext, uuid } from '../../utils/common'; + +test.use({ storageState: 'playwright/.auth/admin.json' }); + +test.describe('Pipeline validation', () => { + let apiContext: APIRequestContext; + let afterAction: (() => Promise) | undefined; + let serviceFqn = ''; + + test.beforeAll(async ({ browser }) => { + const response = await getDefaultAdminAPIContext(browser); + + apiContext = response.apiContext; + afterAction = response.afterAction; + + const serviceName = `pw-pipeline-validation-service-${uuid()}`; + const serviceResponse = await apiContext.post('/api/v1/services/pipelineServices', { + data: { + name: serviceName, + serviceType: 'Dagster', + connection: { + config: { + type: 'Dagster', + host: 'admin', + token: 'admin', + timeout: '1000', + supportsMetadataExtraction: true, + }, + }, + }, + }); + + expect(serviceResponse.status()).toBe(201); + const serviceData = await serviceResponse.json(); + serviceFqn = serviceData.fullyQualifiedName; + }); + + test.afterAll(async () => { + if (serviceFqn) { + await apiContext.delete( + `/api/v1/services/pipelineServices/name/${encodeURIComponent( + serviceFqn + )}?recursive=true&hardDelete=true` + ); + } + await afterAction?.(); + }); + + test('should reject pipeline creation when a task name is empty', async () => { + const pipelineName = `pw-pipeline-validation-${uuid()}`; + const response = await apiContext.post('/api/v1/pipelines', { + data: { + name: pipelineName, + service: serviceFqn, + tasks: [{ name: '' }], + }, + }); + + expect(response.status()).toBe(400); + const responseBody = await response.json(); + expect(responseBody.message || responseBody.error).toBeDefined(); + }); +}); From e4cb1113b477334dc6d43d3661627c0e5c1b439f Mon Sep 17 00:00:00 2001 From: JAYA DUBEY Date: Tue, 28 Apr 2026 21:52:26 +0530 Subject: [PATCH 09/21] chore: add missing license header to PipelineResourceIT --- .../openmetadata/it/tests/PipelineResourceIT.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/PipelineResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/PipelineResourceIT.java index 7e6443b47f48..579dfd00a1db 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/PipelineResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/PipelineResourceIT.java @@ -1,3 +1,16 @@ +/* + * Copyright 2022 Collate. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://apache.org + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package org.openmetadata.it.tests; import static org.junit.jupiter.api.Assertions.assertEquals; From 4229b00f7200317d1c88af796f53cbcf5ad249af Mon Sep 17 00:00:00 2001 From: JAYA DUBEY Date: Tue, 28 Apr 2026 22:06:53 +0530 Subject: [PATCH 10/21] chore: fix license headers and playwright linting --- .../ui/playwright/e2e/Pages/PipelineValidation.spec.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/PipelineValidation.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/PipelineValidation.spec.ts index 2c28f0527e2e..309fcd622890 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/PipelineValidation.spec.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/PipelineValidation.spec.ts @@ -1,9 +1,9 @@ /* - * Copyright 2026 Collate. + * Copyright 2022 Collate. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 - * + * You may obtain a copy of the License at + * http://apache.org * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. From b0435323f34cc3cb5fd2d5393a0ba8f3e13df1a0 Mon Sep 17 00:00:00 2001 From: JAYA DUBEY Date: Tue, 28 Apr 2026 22:13:17 +0530 Subject: [PATCH 11/21] chore: fix license header URL and format --- .../ui/playwright/e2e/Pages/PipelineValidation.spec.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/PipelineValidation.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/PipelineValidation.spec.ts index 309fcd622890..a05820b07d45 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/PipelineValidation.spec.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/PipelineValidation.spec.ts @@ -3,7 +3,8 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * http://apache.org + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. From 77ca90cc30d69268f3c4753be64f11dedffb8761 Mon Sep 17 00:00:00 2001 From: JAYA DUBEY Date: Tue, 28 Apr 2026 22:34:41 +0530 Subject: [PATCH 12/21] chore: fix playwright test formatting --- .../e2e/Pages/PipelineValidation.spec.ts | 29 ++++++++++--------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/PipelineValidation.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/PipelineValidation.spec.ts index a05820b07d45..ff0098f9aee2 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/PipelineValidation.spec.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/PipelineValidation.spec.ts @@ -29,21 +29,24 @@ test.describe('Pipeline validation', () => { afterAction = response.afterAction; const serviceName = `pw-pipeline-validation-service-${uuid()}`; - const serviceResponse = await apiContext.post('/api/v1/services/pipelineServices', { - data: { - name: serviceName, - serviceType: 'Dagster', - connection: { - config: { - type: 'Dagster', - host: 'admin', - token: 'admin', - timeout: '1000', - supportsMetadataExtraction: true, + const serviceResponse = await apiContext.post( + '/api/v1/services/pipelineServices', + { + data: { + name: serviceName, + serviceType: 'Dagster', + connection: { + config: { + type: 'Dagster', + host: 'admin', + token: 'admin', + timeout: '1000', + supportsMetadataExtraction: true, + }, }, }, - }, - }); + } + ); expect(serviceResponse.status()).toBe(201); const serviceData = await serviceResponse.json(); From 419a2fa2730e9fe993233d7ccb8e4c5f98562dcc Mon Sep 17 00:00:00 2001 From: JAYA DUBEY Date: Tue, 28 Apr 2026 22:44:47 +0530 Subject: [PATCH 13/21] chore: fix missing blank line in license headers --- .../src/main/resources/ui/src/constants/regex.constants.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/openmetadata-ui/src/main/resources/ui/src/constants/regex.constants.ts b/openmetadata-ui/src/main/resources/ui/src/constants/regex.constants.ts index 3e773aa260e5..1a04db6a5b27 100644 --- a/openmetadata-ui/src/main/resources/ui/src/constants/regex.constants.ts +++ b/openmetadata-ui/src/main/resources/ui/src/constants/regex.constants.ts @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. From 76867484f33f53d5cf80d4b65d57e5a978912af9 Mon Sep 17 00:00:00 2001 From: JAYA DUBEY Date: Tue, 28 Apr 2026 23:02:27 +0530 Subject: [PATCH 14/21] chore: align license headers with project 2026 standards --- .../ui/playwright/e2e/Pages/PipelineValidation.spec.ts | 2 +- .../main/resources/ui/src/constants/regex.constants.test.ts | 4 +++- .../src/main/resources/ui/src/constants/regex.constants.ts | 4 ++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/PipelineValidation.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/PipelineValidation.spec.ts index ff0098f9aee2..fa7fbf5ac438 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/PipelineValidation.spec.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/PipelineValidation.spec.ts @@ -1,5 +1,5 @@ /* - * Copyright 2022 Collate. + * Copyright 2026 Collate. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at diff --git a/openmetadata-ui/src/main/resources/ui/src/constants/regex.constants.test.ts b/openmetadata-ui/src/main/resources/ui/src/constants/regex.constants.test.ts index e47e5e75fa8e..5701b9637c91 100644 --- a/openmetadata-ui/src/main/resources/ui/src/constants/regex.constants.test.ts +++ b/openmetadata-ui/src/main/resources/ui/src/constants/regex.constants.test.ts @@ -1,15 +1,17 @@ /* - * Copyright 2023 Collate. + * Copyright 2026 Collate. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ + import { ENTITY_NAME_REGEX, TAG_NAME_REGEX, diff --git a/openmetadata-ui/src/main/resources/ui/src/constants/regex.constants.ts b/openmetadata-ui/src/main/resources/ui/src/constants/regex.constants.ts index 1a04db6a5b27..055e8d16716d 100644 --- a/openmetadata-ui/src/main/resources/ui/src/constants/regex.constants.ts +++ b/openmetadata-ui/src/main/resources/ui/src/constants/regex.constants.ts @@ -1,10 +1,10 @@ /* - * Copyright 2022 Collate. + * Copyright 2026 Collate. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. From 602b7a934d1d0f22eac33124a31c7ba41c808dd3 Mon Sep 17 00:00:00 2001 From: JAYA DUBEY Date: Wed, 29 Apr 2026 15:20:27 +0530 Subject: [PATCH 15/21] fix: remove incorrect license header, fix playwright spec, revert copyright year --- .../it/tests/PipelineResourceIT.java | 13 ----- .../e2e/Pages/PipelineValidation.spec.ts | 48 +++++++++++-------- .../ui/src/constants/regex.constants.test.ts | 4 +- 3 files changed, 29 insertions(+), 36 deletions(-) diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/PipelineResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/PipelineResourceIT.java index 579dfd00a1db..7e6443b47f48 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/PipelineResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/PipelineResourceIT.java @@ -1,16 +1,3 @@ -/* - * Copyright 2022 Collate. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://apache.org - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package org.openmetadata.it.tests; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/PipelineValidation.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/PipelineValidation.spec.ts index fa7fbf5ac438..98b043925604 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/PipelineValidation.spec.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/PipelineValidation.spec.ts @@ -1,15 +1,15 @@ /* - * Copyright 2026 Collate. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 + * Copyright 2026 Collate. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ import { expect, test, type APIRequestContext } from '@playwright/test'; @@ -17,14 +17,13 @@ import { getDefaultAdminAPIContext, uuid } from '../../utils/common'; test.use({ storageState: 'playwright/.auth/admin.json' }); -test.describe('Pipeline validation', () => { +test.describe('Pipeline entity name validation', () => { let apiContext: APIRequestContext; - let afterAction: (() => Promise) | undefined; + let afterAction: () => Promise; let serviceFqn = ''; test.beforeAll(async ({ browser }) => { const response = await getDefaultAdminAPIContext(browser); - apiContext = response.apiContext; afterAction = response.afterAction; @@ -61,21 +60,30 @@ test.describe('Pipeline validation', () => { )}?recursive=true&hardDelete=true` ); } - await afterAction?.(); + await afterAction(); }); - test('should reject pipeline creation when a task name is empty', async () => { - const pipelineName = `pw-pipeline-validation-${uuid()}`; + test('should reject pipeline creation when task name is empty', async () => { const response = await apiContext.post('/api/v1/pipelines', { data: { - name: pipelineName, + name: `pw-pipeline-empty-task-${uuid()}`, service: serviceFqn, tasks: [{ name: '' }], }, }); expect(response.status()).toBe(400); - const responseBody = await response.json(); - expect(responseBody.message || responseBody.error).toBeDefined(); }); -}); + + test('should reject pipeline creation when task name contains reserved FQN characters', async () => { + const response = await apiContext.post('/api/v1/pipelines', { + data: { + name: `pw-pipeline-invalid-task-${uuid()}`, + service: serviceFqn, + tasks: [{ name: 'task Date: Wed, 29 Apr 2026 16:18:24 +0530 Subject: [PATCH 16/21] fix: correct license headers and line endings across UI files --- .../e2e/Pages/PipelineValidation.spec.ts | 21 ++- .../ui/src/constants/regex.constants.ts | 160 ++++++++---------- 2 files changed, 84 insertions(+), 97 deletions(-) diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/PipelineValidation.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/PipelineValidation.spec.ts index 98b043925604..943d0b37ce1c 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/PipelineValidation.spec.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/PipelineValidation.spec.ts @@ -1,15 +1,14 @@ /* - * Copyright 2026 Collate. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Copyright 2026 Collate. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ import { expect, test, type APIRequestContext } from '@playwright/test'; diff --git a/openmetadata-ui/src/main/resources/ui/src/constants/regex.constants.ts b/openmetadata-ui/src/main/resources/ui/src/constants/regex.constants.ts index 055e8d16716d..223449b39bf1 100644 --- a/openmetadata-ui/src/main/resources/ui/src/constants/regex.constants.ts +++ b/openmetadata-ui/src/main/resources/ui/src/constants/regex.constants.ts @@ -1,86 +1,74 @@ -/* - * Copyright 2026 Collate. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { ADMONITION_TYPES } from './BlockEditor.constants'; - -export const UrlEntityCharRegEx = /[#.%;?/\\]/g; -export const EMAIL_REG_EX = /^\S+@\S+\.\S+$/; - -/** - * Validates entity names. Blocks reserved FQN separator characters (::, >, <, ", |) - * and ASCII control characters. Supports Unicode characters. - */ -// eslint-disable-next-line no-control-regex -export const ENTITY_NAME_REGEX = /^((?!::)[^><"|\u0000-\u001f])*$/; - -/** - * Matches any string that does NOT contain the following: - * - Double colon (::) - * - Double quote (") - * - Greater-than symbol (>) - * Useful for restricting names from including these forbidden characters. - */ -export const TEST_CASE_NAME_REGEX = /^((?!::)(?!")(?!>).)*$/; - -export const TAG_NAME_REGEX = /^[\p{L}\p{M}\w\- .&()]+$/u; -export const NAME_LENGTH_REGEX = /^.{2,64}$/; - -export const passwordRegex = - /^(?=.*\d)(?=.*[A-Z])(?=.*[a-z])(?=.*[^\w\d\s:])([^\s]){8,56}$/g; - -export const ONEOF_ANYOF_ALLOF_REGEX = /(oneof|anyof|allof)/; - -export const markdownTextAndIdRegex = /^(\S.*?)\s*\$\(id="(.*?)"\)/; -export const MARKDOWN_MATCH_ID = /\$\(id="(.*?)"\)/; - -export const ENDS_WITH_NUMBER_REGEX = /\d+$/; - -export const HEX_COLOR_CODE_REGEX = /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/; - -export const TASK_SANITIZE_VALUE_REGEX = /^"|"$/g; - -export const TIMESTAMP_UNIX_IN_MILLISECONDS_REGEX = /^\d{13}$/; - -export const ALL_ASTERISKS_REGEX = /^\*+$/; - -// Split the input into pairs using `;` and handle quoted strings properly -export const SEMICOLON_SPLITTER = /;(?=(?:(?:[^"]*"){2})*[^"]*$)/; - -// Use regex to check if the string starts and ends with escape characters -export const VALIDATE_ESCAPE_START_END_REGEX = /^(\\+|"+)([\s\S]*?)(\\+|"+)$/; - -// Validates decimal numbers between 0 and 1 (inclusive) -// Matches: 0, 0.5, 0.123, 1, 1.0, 1.00, etc. -export const DECIMAL_ZERO_TO_ONE_REGEX = /^(0(\.\d+)?|1(\.0+)?)$/; - -// Validates integers between 0 and 100 (inclusive) -// Matches: 0, 1, 9, 10, 50, 99, 100 -export const INTEGER_ZERO_TO_HUNDRED_REGEX = /^(100|[1-9]?\d)$/; - -// Validates locale/language codes (ISO 639-1 format) -// Matches: en, fr, de, en-US, fr-CA, de-DE, etc. -// Format: two lowercase letters optionally followed by hyphen and two uppercase letters -export const LOCALE_CODE_REGEX = /^[a-z]{2}(-[A-Z]{2})?$/; - -// Matches URLs (http/https with content, absolute paths with content, data URIs) OR filenames with image extensions -// Filenames restricted to alphanumeric, hyphens, underscores, and dots for security -export const IMAGE_URL_PATTERN = - /^(https?:\/\/.+|\/[^\s]+|data:image\/.+)|^[\w\-.]+\.(png|jpg|jpeg|gif|svg|webp|bmp|ico)$/i; - -export const SECTION_BLOCK_REGEX = /\$\$section\n([\s\S]*?)\n\$\$/g; - -export const ADMONITION_BLOCK_REGEX = new RegExp( - `^\\$\\$(${ADMONITION_TYPES.join('|')})\\n([\\s\\S]*?)\\n\\$\\$`, - 'gm' -); +/* + * Copyright 2022 Collate. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ADMONITION_TYPES } from './BlockEditor.constants'; + +export const UrlEntityCharRegEx = /[#.%;?/\\]/g; +export const EMAIL_REG_EX = /^\S+@\S+\.\S+$/; + +/** + * Validates entity names. Blocks reserved FQN separator characters (::, >, <, ", |) + * and ASCII control characters. Supports Unicode characters. + */ +// eslint-disable-next-line no-control-regex +export const ENTITY_NAME_REGEX = /^((?!::)[^><"|\u0000-\u001f])*$/; + +/** + * Matches any string that does NOT contain the following: + * - Double colon (::) + * - Double quote (") + * - Greater-than symbol (>) + * Useful for restricting names from including these forbidden characters. + */ +export const TEST_CASE_NAME_REGEX = /^((?!::)(?!")(?!>).)*$/; + +export const TAG_NAME_REGEX = /^[\p{L}\p{M}\w\- .&()]+$/u; +export const NAME_LENGTH_REGEX = /^.{2,64}$/; + +export const passwordRegex = + /^(?=.*\d)(?=.*[A-Z])(?=.*[a-z])(?=.*[^\w\d\s:])([^\s]){8,56}$/g; + +export const ONEOF_ANYOF_ALLOF_REGEX = /(oneof|anyof|allof)/; + +export const markdownTextAndIdRegex = /^(\S.*?)\s*\$\(id="(.*?)"\)/; +export const MARKDOWN_MATCH_ID = /\$\(id="(.*?)"\)/; + +export const ENDS_WITH_NUMBER_REGEX = /\d+$/; + +export const HEX_COLOR_CODE_REGEX = /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/; + +export const TASK_SANITIZE_VALUE_REGEX = /^"|"$/g; + +export const TIMESTAMP_UNIX_IN_MILLISECONDS_REGEX = /^\d{13}$/; + +export const ALL_ASTERISKS_REGEX = /^\*+$/; + +export const SEMICOLON_SPLITTER = /;(?=(?:(?:[^"]*"){2})*[^"]*$)/; + +export const VALIDATE_ESCAPE_START_END_REGEX = /^(\\+|"+)([\s\S]*?)(\\+|"+)$/; + +export const DECIMAL_ZERO_TO_ONE_REGEX = /^(0(\.\d+)?|1(\.0+)?)$/; + +export const INTEGER_ZERO_TO_HUNDRED_REGEX = /^(100|[1-9]?\d)$/; + +export const LOCALE_CODE_REGEX = /^[a-z]{2}(-[A-Z]{2})?$/; + +export const IMAGE_URL_PATTERN = + /^(https?:\/\/.+|\/[^\s]+|data:image\/.+)|^[\w\-.]+\.(png|jpg|jpeg|gif|svg|webp|bmp|ico)$/i; + +export const SECTION_BLOCK_REGEX = /\$\$section\n([\s\S]*?)\n\$\$/g; + +export const ADMONITION_BLOCK_REGEX = new RegExp( + `^\\$\\$(${ADMONITION_TYPES.join('|')})\\n([\\s\\S]*?)\\n\\$\\$`, + 'gm' +); From 51a857ea86aabd213fd61b64535434f50e22202c Mon Sep 17 00:00:00 2001 From: JAYA DUBEY Date: Wed, 29 Apr 2026 16:41:05 +0530 Subject: [PATCH 17/21] fix: fix line endings in regex constants --- .../ui/src/constants/regex.constants.ts | 148 +++++++++--------- 1 file changed, 74 insertions(+), 74 deletions(-) diff --git a/openmetadata-ui/src/main/resources/ui/src/constants/regex.constants.ts b/openmetadata-ui/src/main/resources/ui/src/constants/regex.constants.ts index 223449b39bf1..b8364aea17ae 100644 --- a/openmetadata-ui/src/main/resources/ui/src/constants/regex.constants.ts +++ b/openmetadata-ui/src/main/resources/ui/src/constants/regex.constants.ts @@ -1,74 +1,74 @@ -/* - * Copyright 2022 Collate. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { ADMONITION_TYPES } from './BlockEditor.constants'; - -export const UrlEntityCharRegEx = /[#.%;?/\\]/g; -export const EMAIL_REG_EX = /^\S+@\S+\.\S+$/; - -/** - * Validates entity names. Blocks reserved FQN separator characters (::, >, <, ", |) - * and ASCII control characters. Supports Unicode characters. - */ -// eslint-disable-next-line no-control-regex -export const ENTITY_NAME_REGEX = /^((?!::)[^><"|\u0000-\u001f])*$/; - -/** - * Matches any string that does NOT contain the following: - * - Double colon (::) - * - Double quote (") - * - Greater-than symbol (>) - * Useful for restricting names from including these forbidden characters. - */ -export const TEST_CASE_NAME_REGEX = /^((?!::)(?!")(?!>).)*$/; - -export const TAG_NAME_REGEX = /^[\p{L}\p{M}\w\- .&()]+$/u; -export const NAME_LENGTH_REGEX = /^.{2,64}$/; - -export const passwordRegex = - /^(?=.*\d)(?=.*[A-Z])(?=.*[a-z])(?=.*[^\w\d\s:])([^\s]){8,56}$/g; - -export const ONEOF_ANYOF_ALLOF_REGEX = /(oneof|anyof|allof)/; - -export const markdownTextAndIdRegex = /^(\S.*?)\s*\$\(id="(.*?)"\)/; -export const MARKDOWN_MATCH_ID = /\$\(id="(.*?)"\)/; - -export const ENDS_WITH_NUMBER_REGEX = /\d+$/; - -export const HEX_COLOR_CODE_REGEX = /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/; - -export const TASK_SANITIZE_VALUE_REGEX = /^"|"$/g; - -export const TIMESTAMP_UNIX_IN_MILLISECONDS_REGEX = /^\d{13}$/; - -export const ALL_ASTERISKS_REGEX = /^\*+$/; - -export const SEMICOLON_SPLITTER = /;(?=(?:(?:[^"]*"){2})*[^"]*$)/; - -export const VALIDATE_ESCAPE_START_END_REGEX = /^(\\+|"+)([\s\S]*?)(\\+|"+)$/; - -export const DECIMAL_ZERO_TO_ONE_REGEX = /^(0(\.\d+)?|1(\.0+)?)$/; - -export const INTEGER_ZERO_TO_HUNDRED_REGEX = /^(100|[1-9]?\d)$/; - -export const LOCALE_CODE_REGEX = /^[a-z]{2}(-[A-Z]{2})?$/; - -export const IMAGE_URL_PATTERN = - /^(https?:\/\/.+|\/[^\s]+|data:image\/.+)|^[\w\-.]+\.(png|jpg|jpeg|gif|svg|webp|bmp|ico)$/i; - -export const SECTION_BLOCK_REGEX = /\$\$section\n([\s\S]*?)\n\$\$/g; - -export const ADMONITION_BLOCK_REGEX = new RegExp( - `^\\$\\$(${ADMONITION_TYPES.join('|')})\\n([\\s\\S]*?)\\n\\$\\$`, - 'gm' -); +/* + * Copyright 2022 Collate. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ADMONITION_TYPES } from './BlockEditor.constants'; + +export const UrlEntityCharRegEx = /[#.%;?/\\]/g; +export const EMAIL_REG_EX = /^\S+@\S+\.\S+$/; + +/** + * Validates entity names. Blocks reserved FQN separator characters (::, >, <, ", |) + * and ASCII control characters. Supports Unicode characters. + */ +// eslint-disable-next-line no-control-regex +export const ENTITY_NAME_REGEX = /^((?!::)[^><"|\u0000-\u001f])*$/; + +/** + * Matches any string that does NOT contain the following: + * - Double colon (::) + * - Double quote (") + * - Greater-than symbol (>) + * Useful for restricting names from including these forbidden characters. + */ +export const TEST_CASE_NAME_REGEX = /^((?!::)(?!")(?!>).)*$/; + +export const TAG_NAME_REGEX = /^[\p{L}\p{M}\w\- .&()]+$/u; +export const NAME_LENGTH_REGEX = /^.{2,64}$/; + +export const passwordRegex = + /^(?=.*\d)(?=.*[A-Z])(?=.*[a-z])(?=.*[^\w\d\s:])([^\s]){8,56}$/g; + +export const ONEOF_ANYOF_ALLOF_REGEX = /(oneof|anyof|allof)/; + +export const markdownTextAndIdRegex = /^(\S.*?)\s*\$\(id="(.*?)"\)/; +export const MARKDOWN_MATCH_ID = /\$\(id="(.*?)"\)/; + +export const ENDS_WITH_NUMBER_REGEX = /\d+$/; + +export const HEX_COLOR_CODE_REGEX = /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/; + +export const TASK_SANITIZE_VALUE_REGEX = /^"|"$/g; + +export const TIMESTAMP_UNIX_IN_MILLISECONDS_REGEX = /^\d{13}$/; + +export const ALL_ASTERISKS_REGEX = /^\*+$/; + +export const SEMICOLON_SPLITTER = /;(?=(?:(?:[^"]*"){2})*[^"]*$)/; + +export const VALIDATE_ESCAPE_START_END_REGEX = /^(\\+|"+)([\s\S]*?)(\\+|"+)$/; + +export const DECIMAL_ZERO_TO_ONE_REGEX = /^(0(\.\d+)?|1(\.0+)?)$/; + +export const INTEGER_ZERO_TO_HUNDRED_REGEX = /^(100|[1-9]?\d)$/; + +export const LOCALE_CODE_REGEX = /^[a-z]{2}(-[A-Z]{2})?$/; + +export const IMAGE_URL_PATTERN = + /^(https?:\/\/.+|\/[^\s]+|data:image\/.+)|^[\w\-.]+\.(png|jpg|jpeg|gif|svg|webp|bmp|ico)$/i; + +export const SECTION_BLOCK_REGEX = /\$\$section\n([\s\S]*?)\n\$\$/g; + +export const ADMONITION_BLOCK_REGEX = new RegExp( + `^\\$\\$(${ADMONITION_TYPES.join('|')})\\n([\\s\\S]*?)\\n\\$\\$`, + 'gm' +); From cf2f659816bfed4078f8b1e34451248a268a657b Mon Sep 17 00:00:00 2001 From: JAYA DUBEY Date: Wed, 29 Apr 2026 16:51:24 +0530 Subject: [PATCH 18/21] fix: fix line endings and formatting in PipelineValidation spec --- .../ui/playwright/e2e/Pages/PipelineValidation.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/PipelineValidation.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/PipelineValidation.spec.ts index 943d0b37ce1c..970f96c12dc8 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/PipelineValidation.spec.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/PipelineValidation.spec.ts @@ -85,4 +85,4 @@ test.describe('Pipeline entity name validation', () => { expect(response.status()).toBe(400); }); -}); \ No newline at end of file +}); From cde2888e54543843284377892ff54a850145b4d5 Mon Sep 17 00:00:00 2001 From: JAYA DUBEY Date: Wed, 29 Apr 2026 21:22:15 +0530 Subject: [PATCH 19/21] fix: replace lookahead regex with character class to fix Python/Rust compatibility --- .../main/resources/json/schema/entity/data/pipeline.json | 2 +- .../main/resources/json/schema/entity/data/searchIndex.json | 2 +- .../src/main/resources/json/schema/entity/data/table.json | 6 +++--- .../src/main/resources/json/schema/type/basic.json | 4 ++-- .../src/main/resources/json/schema/type/schema.json | 2 +- .../main/resources/ui/src/constants/regex.constants.test.ts | 2 ++ .../src/main/resources/ui/src/constants/regex.constants.ts | 2 +- 7 files changed, 11 insertions(+), 9 deletions(-) diff --git a/openmetadata-spec/src/main/resources/json/schema/entity/data/pipeline.json b/openmetadata-spec/src/main/resources/json/schema/entity/data/pipeline.json index 44bd6a02a27c..d5f0ddd1d3dc 100644 --- a/openmetadata-spec/src/main/resources/json/schema/entity/data/pipeline.json +++ b/openmetadata-spec/src/main/resources/json/schema/entity/data/pipeline.json @@ -109,7 +109,7 @@ "description": "Name that identifies this task instance uniquely.", "type": "string", "minLength": 1, - "pattern": "^((?!::)[^><\"|\\x00-\\x1f])*$" + "pattern": "^[^><\":|\\x00-\\x1f]*$" }, "displayName": { "description": "Display Name that identifies this Task. It could be title or label from the pipeline services.", diff --git a/openmetadata-spec/src/main/resources/json/schema/entity/data/searchIndex.json b/openmetadata-spec/src/main/resources/json/schema/entity/data/searchIndex.json index 6ebcd116b538..26710165fc3f 100644 --- a/openmetadata-spec/src/main/resources/json/schema/entity/data/searchIndex.json +++ b/openmetadata-spec/src/main/resources/json/schema/entity/data/searchIndex.json @@ -97,7 +97,7 @@ "type": "string", "minLength": 1, "maxLength": 256, - "pattern": "^((?!::)[^><\"|\\x00-\\x1f])*$" + "pattern": "^[^><\":|\\x00-\\x1f]*$" }, "searchIndexField": { "type": "object", diff --git a/openmetadata-spec/src/main/resources/json/schema/entity/data/table.json b/openmetadata-spec/src/main/resources/json/schema/entity/data/table.json index c6754c0b3cf7..c3301dd8a49b 100644 --- a/openmetadata-spec/src/main/resources/json/schema/entity/data/table.json +++ b/openmetadata-spec/src/main/resources/json/schema/entity/data/table.json @@ -209,7 +209,7 @@ "type": "array", "items": { "type": "string", - "pattern": "^((?!::)[^><\"|\\x00-\\x1f])*$" + "pattern": "^[^><\":|\\x00-\\x1f]*$" } }, "referredColumns": { @@ -236,7 +236,7 @@ "description": "Local name (not fully qualified name) of the column. ColumnName is `-` when the column is not named in struct dataType. For example, BigQuery supports struct with unnamed fields.", "type": "string", "minLength": 1, - "pattern": "^((?!::)[^><\"|\\x00-\\x1f])*$" + "pattern": "^[^><\":|\\x00-\\x1f]*$" }, "partitionIntervalTypes": { "javaType": "org.openmetadata.schema.type.PartitionIntervalTypes", @@ -275,7 +275,7 @@ "columnName": { "description": "List of column names corresponding to the partition.", "type": "string", - "pattern": "^((?!::)[^><\"|\\x00-\\x1f])*$" + "pattern": "^[^><\":|\\x00-\\x1f]*$" }, "intervalType": { "$ref": "#/definitions/partitionIntervalTypes" diff --git a/openmetadata-spec/src/main/resources/json/schema/type/basic.json b/openmetadata-spec/src/main/resources/json/schema/type/basic.json index 22996b882adb..4a18bff7d41e 100644 --- a/openmetadata-spec/src/main/resources/json/schema/type/basic.json +++ b/openmetadata-spec/src/main/resources/json/schema/type/basic.json @@ -124,13 +124,13 @@ "type": "string", "minLength": 1, "maxLength": 256, - "pattern": "^((?!::)[^><\"|\\x00-\\x1f])*$" + "pattern": "^[^><\":|\\x00-\\x1f]*$" }, "testCaseEntityName": { "description": "Name that identifies a test definition and test case.", "type": "string", "minLength": 1, - "pattern": "^((?!::)[^><\"|\\x00-\\x1f])*$" + "pattern": "^[^><\":|\\x00-\\x1f]*$" }, "fullyQualifiedEntityName": { "description": "A unique name that identifies an entity. Example for table 'DatabaseService.Database.Schema.Table'.", diff --git a/openmetadata-spec/src/main/resources/json/schema/type/schema.json b/openmetadata-spec/src/main/resources/json/schema/type/schema.json index 06fde346fbe6..1b500e98c781 100644 --- a/openmetadata-spec/src/main/resources/json/schema/type/schema.json +++ b/openmetadata-spec/src/main/resources/json/schema/type/schema.json @@ -67,7 +67,7 @@ "type": "string", "minLength": 1, "maxLength": 128, - "pattern": "^((?!::)[^><\"|\\x00-\\x1f])*$" + "pattern": "^[^><\":|\\x00-\\x1f]*$" }, "field": { "type": "object", diff --git a/openmetadata-ui/src/main/resources/ui/src/constants/regex.constants.test.ts b/openmetadata-ui/src/main/resources/ui/src/constants/regex.constants.test.ts index e47e5e75fa8e..47e6720f1d6b 100644 --- a/openmetadata-ui/src/main/resources/ui/src/constants/regex.constants.test.ts +++ b/openmetadata-ui/src/main/resources/ui/src/constants/regex.constants.test.ts @@ -162,6 +162,8 @@ describe('Test Regex', () => { it('EntityName regex should fail for the invalid entity name', () => { // conatines :: in the name should fail expect(ENTITY_NAME_REGEX.test('Hello::World')).toEqual(false); + // single colon also blocked as FQN separator + expect(ENTITY_NAME_REGEX.test('name:bad')).toEqual(false); expect(ENTITY_NAME_REGEX.test('name>bad')).toEqual(false); expect(ENTITY_NAME_REGEX.test('name<"|\u0000-\u001f])*$/; +export const ENTITY_NAME_REGEX = /^[^><"|:\u0000-\u001f]*$/; /** * Matches any string that does NOT contain the following: From 4f11e09ca3ad84bcea8d054272f0e63094eab70b Mon Sep 17 00:00:00 2001 From: JAYA DUBEY Date: Thu, 30 Apr 2026 01:23:47 +0530 Subject: [PATCH 20/21] fix: use character class regex without lookahead for Python/Rust compatibility --- .../resources/json/schema/entity/data/pipeline.json | 2 +- .../json/schema/entity/data/searchIndex.json | 2 +- .../resources/json/schema/entity/data/table.json | 6 +++--- .../src/main/resources/json/schema/type/basic.json | 4 ++-- .../src/main/resources/json/schema/type/schema.json | 2 +- .../ui/src/constants/regex.constants.test.ts | 6 ++---- .../resources/ui/src/constants/regex.constants.ts | 13 ++++++++++++- 7 files changed, 22 insertions(+), 13 deletions(-) diff --git a/openmetadata-spec/src/main/resources/json/schema/entity/data/pipeline.json b/openmetadata-spec/src/main/resources/json/schema/entity/data/pipeline.json index d5f0ddd1d3dc..7d22300551a6 100644 --- a/openmetadata-spec/src/main/resources/json/schema/entity/data/pipeline.json +++ b/openmetadata-spec/src/main/resources/json/schema/entity/data/pipeline.json @@ -109,7 +109,7 @@ "description": "Name that identifies this task instance uniquely.", "type": "string", "minLength": 1, - "pattern": "^[^><\":|\\x00-\\x1f]*$" + "pattern": "^[^><\"|\\x00-\\x1f]*$" }, "displayName": { "description": "Display Name that identifies this Task. It could be title or label from the pipeline services.", diff --git a/openmetadata-spec/src/main/resources/json/schema/entity/data/searchIndex.json b/openmetadata-spec/src/main/resources/json/schema/entity/data/searchIndex.json index 26710165fc3f..59afd338bd2f 100644 --- a/openmetadata-spec/src/main/resources/json/schema/entity/data/searchIndex.json +++ b/openmetadata-spec/src/main/resources/json/schema/entity/data/searchIndex.json @@ -97,7 +97,7 @@ "type": "string", "minLength": 1, "maxLength": 256, - "pattern": "^[^><\":|\\x00-\\x1f]*$" + "pattern": "^[^><\"|\\x00-\\x1f]*$" }, "searchIndexField": { "type": "object", diff --git a/openmetadata-spec/src/main/resources/json/schema/entity/data/table.json b/openmetadata-spec/src/main/resources/json/schema/entity/data/table.json index c3301dd8a49b..8b31a683e3c7 100644 --- a/openmetadata-spec/src/main/resources/json/schema/entity/data/table.json +++ b/openmetadata-spec/src/main/resources/json/schema/entity/data/table.json @@ -209,7 +209,7 @@ "type": "array", "items": { "type": "string", - "pattern": "^[^><\":|\\x00-\\x1f]*$" + "pattern": "^[^><\"|\\x00-\\x1f]*$" } }, "referredColumns": { @@ -236,7 +236,7 @@ "description": "Local name (not fully qualified name) of the column. ColumnName is `-` when the column is not named in struct dataType. For example, BigQuery supports struct with unnamed fields.", "type": "string", "minLength": 1, - "pattern": "^[^><\":|\\x00-\\x1f]*$" + "pattern": "^[^><\"|\\x00-\\x1f]*$" }, "partitionIntervalTypes": { "javaType": "org.openmetadata.schema.type.PartitionIntervalTypes", @@ -275,7 +275,7 @@ "columnName": { "description": "List of column names corresponding to the partition.", "type": "string", - "pattern": "^[^><\":|\\x00-\\x1f]*$" + "pattern": "^[^><\"|\\x00-\\x1f]*$" }, "intervalType": { "$ref": "#/definitions/partitionIntervalTypes" diff --git a/openmetadata-spec/src/main/resources/json/schema/type/basic.json b/openmetadata-spec/src/main/resources/json/schema/type/basic.json index 4a18bff7d41e..660cab5945a4 100644 --- a/openmetadata-spec/src/main/resources/json/schema/type/basic.json +++ b/openmetadata-spec/src/main/resources/json/schema/type/basic.json @@ -124,13 +124,13 @@ "type": "string", "minLength": 1, "maxLength": 256, - "pattern": "^[^><\":|\\x00-\\x1f]*$" + "pattern": "^[^><\"|\\x00-\\x1f]*$" }, "testCaseEntityName": { "description": "Name that identifies a test definition and test case.", "type": "string", "minLength": 1, - "pattern": "^[^><\":|\\x00-\\x1f]*$" + "pattern": "^[^><\"|\\x00-\\x1f]*$" }, "fullyQualifiedEntityName": { "description": "A unique name that identifies an entity. Example for table 'DatabaseService.Database.Schema.Table'.", diff --git a/openmetadata-spec/src/main/resources/json/schema/type/schema.json b/openmetadata-spec/src/main/resources/json/schema/type/schema.json index 1b500e98c781..c245061f1337 100644 --- a/openmetadata-spec/src/main/resources/json/schema/type/schema.json +++ b/openmetadata-spec/src/main/resources/json/schema/type/schema.json @@ -67,7 +67,7 @@ "type": "string", "minLength": 1, "maxLength": 128, - "pattern": "^[^><\":|\\x00-\\x1f]*$" + "pattern": "^[^><\"|\\x00-\\x1f]*$" }, "field": { "type": "object", diff --git a/openmetadata-ui/src/main/resources/ui/src/constants/regex.constants.test.ts b/openmetadata-ui/src/main/resources/ui/src/constants/regex.constants.test.ts index 47e6720f1d6b..fa6e570de389 100644 --- a/openmetadata-ui/src/main/resources/ui/src/constants/regex.constants.test.ts +++ b/openmetadata-ui/src/main/resources/ui/src/constants/regex.constants.test.ts @@ -160,13 +160,11 @@ describe('Test Regex', () => { }); it('EntityName regex should fail for the invalid entity name', () => { - // conatines :: in the name should fail - expect(ENTITY_NAME_REGEX.test('Hello::World')).toEqual(false); - // single colon also blocked as FQN separator - expect(ENTITY_NAME_REGEX.test('name:bad')).toEqual(false); + // contains reserved FQN characters - should fail expect(ENTITY_NAME_REGEX.test('name>bad')).toEqual(false); expect(ENTITY_NAME_REGEX.test('name<"|:\u0000-\u001f]*$/; +export const ENTITY_NAME_REGEX = /^[^><"|\u0000-\u001f]*$/; /** * Matches any string that does NOT contain the following: @@ -53,16 +53,27 @@ export const TIMESTAMP_UNIX_IN_MILLISECONDS_REGEX = /^\d{13}$/; export const ALL_ASTERISKS_REGEX = /^\*+$/; +// Split the input into pairs using `;` and handle quoted strings properly export const SEMICOLON_SPLITTER = /;(?=(?:(?:[^"]*"){2})*[^"]*$)/; +// Use regex to check if the string starts and ends with escape characters export const VALIDATE_ESCAPE_START_END_REGEX = /^(\\+|"+)([\s\S]*?)(\\+|"+)$/; +// Validates decimal numbers between 0 and 1 (inclusive) +// Matches: 0, 0.5, 0.123, 1, 1.0, 1.00, etc. export const DECIMAL_ZERO_TO_ONE_REGEX = /^(0(\.\d+)?|1(\.0+)?)$/; +// Validates integers between 0 and 100 (inclusive) +// Matches: 0, 1, 9, 10, 50, 99, 100 export const INTEGER_ZERO_TO_HUNDRED_REGEX = /^(100|[1-9]?\d)$/; +// Validates locale/language codes (ISO 639-1 format) +// Matches: en, fr, de, en-US, fr-CA, de-DE, etc. +// Format: two lowercase letters optionally followed by hyphen and two uppercase letters export const LOCALE_CODE_REGEX = /^[a-z]{2}(-[A-Z]{2})?$/; +// Matches URLs (http/https with content, absolute paths with content, data URIs) OR filenames with image extensions +// Filenames restricted to alphanumeric, hyphens, underscores, and dots for security export const IMAGE_URL_PATTERN = /^(https?:\/\/.+|\/[^\s]+|data:image\/.+)|^[\w\-.]+\.(png|jpg|jpeg|gif|svg|webp|bmp|ico)$/i; From eee2391adda8b2a6b27e1efa1b7fa76e80610c0a Mon Sep 17 00:00:00 2001 From: JAYA DUBEY Date: Fri, 1 May 2026 00:23:27 +0530 Subject: [PATCH 21/21] fix: re-add double colon blocking in entity name patterns - Block :: (reserved FQN separator) in JSON schema and frontend regex - Keep single colon allowed - Restore :: rejection unit tests --- .../main/resources/json/schema/entity/data/pipeline.json | 2 +- .../resources/json/schema/entity/data/searchIndex.json | 2 +- .../src/main/resources/json/schema/entity/data/table.json | 6 +++--- .../src/main/resources/json/schema/type/basic.json | 4 ++-- .../src/main/resources/json/schema/type/schema.json | 2 +- .../resources/ui/src/constants/regex.constants.test.ts | 8 ++++++++ .../main/resources/ui/src/constants/regex.constants.ts | 2 +- 7 files changed, 17 insertions(+), 9 deletions(-) diff --git a/openmetadata-spec/src/main/resources/json/schema/entity/data/pipeline.json b/openmetadata-spec/src/main/resources/json/schema/entity/data/pipeline.json index 7d22300551a6..44bd6a02a27c 100644 --- a/openmetadata-spec/src/main/resources/json/schema/entity/data/pipeline.json +++ b/openmetadata-spec/src/main/resources/json/schema/entity/data/pipeline.json @@ -109,7 +109,7 @@ "description": "Name that identifies this task instance uniquely.", "type": "string", "minLength": 1, - "pattern": "^[^><\"|\\x00-\\x1f]*$" + "pattern": "^((?!::)[^><\"|\\x00-\\x1f])*$" }, "displayName": { "description": "Display Name that identifies this Task. It could be title or label from the pipeline services.", diff --git a/openmetadata-spec/src/main/resources/json/schema/entity/data/searchIndex.json b/openmetadata-spec/src/main/resources/json/schema/entity/data/searchIndex.json index 59afd338bd2f..6ebcd116b538 100644 --- a/openmetadata-spec/src/main/resources/json/schema/entity/data/searchIndex.json +++ b/openmetadata-spec/src/main/resources/json/schema/entity/data/searchIndex.json @@ -97,7 +97,7 @@ "type": "string", "minLength": 1, "maxLength": 256, - "pattern": "^[^><\"|\\x00-\\x1f]*$" + "pattern": "^((?!::)[^><\"|\\x00-\\x1f])*$" }, "searchIndexField": { "type": "object", diff --git a/openmetadata-spec/src/main/resources/json/schema/entity/data/table.json b/openmetadata-spec/src/main/resources/json/schema/entity/data/table.json index 8b31a683e3c7..c6754c0b3cf7 100644 --- a/openmetadata-spec/src/main/resources/json/schema/entity/data/table.json +++ b/openmetadata-spec/src/main/resources/json/schema/entity/data/table.json @@ -209,7 +209,7 @@ "type": "array", "items": { "type": "string", - "pattern": "^[^><\"|\\x00-\\x1f]*$" + "pattern": "^((?!::)[^><\"|\\x00-\\x1f])*$" } }, "referredColumns": { @@ -236,7 +236,7 @@ "description": "Local name (not fully qualified name) of the column. ColumnName is `-` when the column is not named in struct dataType. For example, BigQuery supports struct with unnamed fields.", "type": "string", "minLength": 1, - "pattern": "^[^><\"|\\x00-\\x1f]*$" + "pattern": "^((?!::)[^><\"|\\x00-\\x1f])*$" }, "partitionIntervalTypes": { "javaType": "org.openmetadata.schema.type.PartitionIntervalTypes", @@ -275,7 +275,7 @@ "columnName": { "description": "List of column names corresponding to the partition.", "type": "string", - "pattern": "^[^><\"|\\x00-\\x1f]*$" + "pattern": "^((?!::)[^><\"|\\x00-\\x1f])*$" }, "intervalType": { "$ref": "#/definitions/partitionIntervalTypes" diff --git a/openmetadata-spec/src/main/resources/json/schema/type/basic.json b/openmetadata-spec/src/main/resources/json/schema/type/basic.json index 660cab5945a4..22996b882adb 100644 --- a/openmetadata-spec/src/main/resources/json/schema/type/basic.json +++ b/openmetadata-spec/src/main/resources/json/schema/type/basic.json @@ -124,13 +124,13 @@ "type": "string", "minLength": 1, "maxLength": 256, - "pattern": "^[^><\"|\\x00-\\x1f]*$" + "pattern": "^((?!::)[^><\"|\\x00-\\x1f])*$" }, "testCaseEntityName": { "description": "Name that identifies a test definition and test case.", "type": "string", "minLength": 1, - "pattern": "^[^><\"|\\x00-\\x1f]*$" + "pattern": "^((?!::)[^><\"|\\x00-\\x1f])*$" }, "fullyQualifiedEntityName": { "description": "A unique name that identifies an entity. Example for table 'DatabaseService.Database.Schema.Table'.", diff --git a/openmetadata-spec/src/main/resources/json/schema/type/schema.json b/openmetadata-spec/src/main/resources/json/schema/type/schema.json index c245061f1337..06fde346fbe6 100644 --- a/openmetadata-spec/src/main/resources/json/schema/type/schema.json +++ b/openmetadata-spec/src/main/resources/json/schema/type/schema.json @@ -67,7 +67,7 @@ "type": "string", "minLength": 1, "maxLength": 128, - "pattern": "^[^><\"|\\x00-\\x1f]*$" + "pattern": "^((?!::)[^><\"|\\x00-\\x1f])*$" }, "field": { "type": "object", diff --git a/openmetadata-ui/src/main/resources/ui/src/constants/regex.constants.test.ts b/openmetadata-ui/src/main/resources/ui/src/constants/regex.constants.test.ts index fa6e570de389..60a7c374ca73 100644 --- a/openmetadata-ui/src/main/resources/ui/src/constants/regex.constants.test.ts +++ b/openmetadata-ui/src/main/resources/ui/src/constants/regex.constants.test.ts @@ -168,6 +168,14 @@ describe('Test Regex', () => { expect(ENTITY_NAME_REGEX.test('name\nbad')).toEqual(false); expect(ENTITY_NAME_REGEX.test('name\rbad')).toEqual(false); expect(ENTITY_NAME_REGEX.test('name\x00bad')).toEqual(false); + + // double colon is reserved FQN separator - should fail + expect(ENTITY_NAME_REGEX.test('Hello::World')).toEqual(false); + }); + + it('EntityName regex should pass for names with single colons', () => { + expect(ENTITY_NAME_REGEX.test('name:value')).toEqual(true); + expect(ENTITY_NAME_REGEX.test('a:b:c:d')).toEqual(true); }); describe('TAG_NAME_REGEX', () => { diff --git a/openmetadata-ui/src/main/resources/ui/src/constants/regex.constants.ts b/openmetadata-ui/src/main/resources/ui/src/constants/regex.constants.ts index 36d9fab4a611..cc3b64dcf819 100644 --- a/openmetadata-ui/src/main/resources/ui/src/constants/regex.constants.ts +++ b/openmetadata-ui/src/main/resources/ui/src/constants/regex.constants.ts @@ -21,7 +21,7 @@ export const EMAIL_REG_EX = /^\S+@\S+\.\S+$/; * and ASCII control characters. Supports Unicode characters. */ // eslint-disable-next-line no-control-regex -export const ENTITY_NAME_REGEX = /^[^><"|\u0000-\u001f]*$/; +export const ENTITY_NAME_REGEX = /^(?!.*::)[^><"|\u0000-\u001f]*$/; /** * Matches any string that does NOT contain the following: