This is an automated email from the ASF dual-hosted git repository.

kinow pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/jena.git


The following commit(s) were added to refs/heads/main by this push:
     new 677a22decf GH-2370: Improve validation of dataset graph names
677a22decf is described below

commit 677a22decfdffbcdbd187dd3223d95343b5dc381
Author: Bruno P. Kinoshita <[email protected]>
AuthorDate: Wed Mar 27 13:00:24 2024 +0100

    GH-2370: Improve validation of dataset graph names
---
 .../jena-fuseki-ui/src/utils/validation.js         | 48 +++++++++++++
 .../jena-fuseki-ui/src/views/dataset/Upload.vue    | 15 ++--
 .../tests/unit/utils/validation.spec.js            | 84 ++++++++++++++++++++++
 3 files changed, 138 insertions(+), 9 deletions(-)

diff --git a/jena-fuseki2/jena-fuseki-ui/src/utils/validation.js 
b/jena-fuseki2/jena-fuseki-ui/src/utils/validation.js
new file mode 100644
index 0000000000..fd45116f91
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-ui/src/utils/validation.js
@@ -0,0 +1,48 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+/*
+ * Utility functions for data validation in the UI.
+ *
+ * We must avoid repeating the validation when the backend server is handling 
this,
+ * unless we really want to do that (avoid large payloads, for security, etc.).
+ */
+
+/**
+ * Validates a Jena UI graph name.
+ *
+ * @param {string} graphName - The name of the graph provided by user or 
configuration file.
+ * @return {boolean} - true iff the given name of the graph provided is valid 
for Jena.
+ */
+export function validateGraphName (graphName) {
+  if (graphName === '' || graphName.trim() === '') {
+    return false
+  }
+  // No spaces allowed in graph names.
+  const pattern = /^\S+$/
+  if (!pattern.test(graphName)) {
+    return false
+  }
+  // Only valid URIs allowed.
+  try {
+    new URL(graphName)
+  } catch {
+    return false
+  }
+  // If it reached this part, then it's a valid graph name.
+  return true
+}
diff --git a/jena-fuseki2/jena-fuseki-ui/src/views/dataset/Upload.vue 
b/jena-fuseki2/jena-fuseki-ui/src/views/dataset/Upload.vue
index 028b2d4f44..2fb2a1e4fe 100644
--- a/jena-fuseki2/jena-fuseki-ui/src/views/dataset/Upload.vue
+++ b/jena-fuseki2/jena-fuseki-ui/src/views/dataset/Upload.vue
@@ -60,7 +60,7 @@
                         placeholder="Leave blank for default graph"
                       />
                       <div class="invalid-feedback">
-                        Invalid graph name. Please remove any spaces.
+                        Invalid graph name. Please remove any spaces, and 
provide a valid URI (RFC 3986).
                       </div>
                     </div>
                   </div>
@@ -221,6 +221,7 @@ import { FontAwesomeIcon } from 
'@fortawesome/vue-fontawesome'
 import currentDatasetMixin from '@/mixins/current-dataset'
 import currentDatasetMixinNavigationGuards from 
'@/mixins/current-dataset-navigation-guards'
 import { displayError } from '@/utils'
+import { validateGraphName } from '@/utils/validation'
 
 library.add(faPlus, faUpload, faTimesCircle, faMinusCircle)
 
@@ -418,15 +419,11 @@ export default {
       return this.validateGraphName() && this.validateFiles()
     },
     validateGraphName () {
-      // No spaces allowed in graph names.
-      const pattern = /^[^\s]+$/
       const graphName = this.$refs['dataset-graph-name'].value
-      if (graphName === '' || pattern.test(graphName)) {
-        this.graphNameClasses = ['form-control is-valid']
-        return true
-      }
-      this.graphNameClasses = ['form-control is-invalid']
-      return false
+      const isValidGraphName = validateGraphName(graphName)
+      const formValidationClass = isValidGraphName ? 'is-valid' : 'is-invalid'
+      this.graphNameClasses = ['form-control', formValidationClass]
+      return isValidGraphName
     },
     validateFiles () {
       if (this.upload.files !== null && this.upload.files.length > 0) {
diff --git a/jena-fuseki2/jena-fuseki-ui/tests/unit/utils/validation.spec.js 
b/jena-fuseki2/jena-fuseki-ui/tests/unit/utils/validation.spec.js
new file mode 100644
index 0000000000..89a6e98fd5
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-ui/tests/unit/utils/validation.spec.js
@@ -0,0 +1,84 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 { describe, expect, it } from 'vitest'
+import { validateGraphName } from '@/utils/validation'
+
+const VALID_GRAPH_NAMES = [
+  // From issue GH-2370 discussion
+  'urn:x-arq:UnionGraph',
+  'urn:x-arq:DefaultGraph',
+  'urn:uuid:6e8bc430-9c3a-11d9-9669-0800200c9a66',
+  'https://example.org/dataset',
+  'https://example.org/dataset#graph',
+  'https://example.org/dataset#graph?Aaa',
+  // From RFC-3986
+  'ftp://ftp.is.co.za/rfc/rfc1808.txt',
+  'http://www.ietf.org/rfc/rfc2396.txt',
+  'ldap://[2001:db8::7]/c=GB?objectClass?one',
+  'mailto:[email protected]',
+  'news:comp.infosystems.www.servers.unix',
+  'tel:+1-816-555-1212',
+  'telnet://192.0.2.16:80/',
+  'urn:oasis:names:specification:docbook:dtd:xml:4.1.2',
+  'foo://example.com:8042/over/there?name=ferret#nose',
+  'example://a/b/c/%7Bfoo%7D',
+  'eXAMPLE://a/./b/../b/%63/%7bfoo%7d',
+  'http://example.com:80/',
+  'ftp://cnn.example.com&[email protected]/top_story.htm',
+  // From URI.js docs
+  'uri://user:[email protected]:123/one/two.three?q1=a1&q2=a2#body',
+  'HTTP://ABC.COM:80',
+  'HTTPS://ABC.COM:443/',
+  'WS://ABC.COM:80/chat#one',
+  
'mailto:[email protected],[email protected]?subject=SUBSCRIBE&body=Sign%20me%20up!',
+  'uri://www.example.org/red%09ros\xE9#red',
+  'uri://www.example.org/red%09ros%C3%A9#red',
+  'uri://www.example.org/D%C3%BCrst',
+  'uri://www.example.org/D\xFCrst',
+  'wss://example.com/foo?bar'
+]
+
+const INVALID_GRAPH_NAMES = [
+  // From issue GH-2370 discussion
+  'snoopy',
+  'test',
+  'default',
+  'wss://example.com/ foo?bar',
+  'https://this is an invalid URL.com',
+  'http%3A//www.example.com/other/graph'
+]
+
+describe('validation', () => {
+  it('Should reject empty graph names', () => {
+    expect(validateGraphName('')).to.equals(false)
+    expect(validateGraphName('  ')).to.equals(false)
+  })
+  it('Should reject graph names that contain spaces', () => {
+    expect(validateGraphName('jena graph')).to.equals(false)
+    expect(validateGraphName('')).to.equals(false)
+  })
+  it('Should reject graph names that are not valid URIs', () => {
+    for (let graphName of INVALID_GRAPH_NAMES) {
+      expect(validateGraphName(graphName)).to.equals(false)
+    }
+  })
+  it('Should accept valid graph names', () => {
+    for (let graphName of VALID_GRAPH_NAMES) {
+      expect(validateGraphName(graphName), `Rejected valid graph name 
"${graphName}"`).to.equals(true)
+    }
+  })
+})

Reply via email to