Skip to content

Commit

Permalink
GH-2370: Improve validation of dataset graph names
Browse files Browse the repository at this point in the history
  • Loading branch information
kinow committed Oct 12, 2024
1 parent 525e717 commit 159aa23
Show file tree
Hide file tree
Showing 3 changed files with 138 additions and 9 deletions.
48 changes: 48 additions & 0 deletions jena-fuseki2/jena-fuseki-ui/src/utils/validation.js
Original file line number Diff line number Diff line change
@@ -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
}
15 changes: 6 additions & 9 deletions jena-fuseki2/jena-fuseki-ui/src/views/dataset/Upload.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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) {
Expand Down
84 changes: 84 additions & 0 deletions jena-fuseki2/jena-fuseki-ui/tests/unit/utils/validation.spec.js
Original file line number Diff line number Diff line change
@@ -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)
}
})
})

0 comments on commit 159aa23

Please sign in to comment.