Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion THREAT_MODEL.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ limitations under the License.
| Stores + text index | `jena-tdb1`, `jena-tdb2`; **`jena-text` (Lucene)** | filesystem | **In.** On-disk store is operator-trusted and private to the owning process *(maintainer)*; the Lucene text index is reachable from SPARQL via `text:query` — an in-model query surface *(maintainer — afs flagged jena-text)* |
| IRI / langtag | `jena-iri3986`, `jena-langtag`, `jena-base` | none | **In (input parsing)** *(inferred)* |
| Extensions | `jena-geosparql`, `jena-serviceenhancer` | SERVICE | **In (reachable from queries)** *(inferred)* |
| Validations | `jena-shacl`, `jena-shex` | HTTP GET requests (imports) | **In (import-fetch = SSRF surface)** *(maintainer — afs)* |
| Validations | `jena-shacl` | HTTP GET requests | **In (no imports)** *(maintainer — afs)* |
| Client/API helpers | `jena-rdfconnection`, `jena-querybuilder`, `jena-rdfpatch`, `jena-commonsrdf`, `jena-ontapi` | none | **In as libraries (memory/correctness)** *(inferred)* |
| CLI tools | `jena-cmds` | filesystem | **In iff fed untrusted input; usually operator-run** *(inferred)* |
| Examples / tests / benchmarks | `jena-examples`, `jena-integration-tests`, `jena-benchmarks` | n/a | **Out** *(see §3)* |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,11 +103,13 @@ public static DatasetGraph controlledDataset(DatasetGraph dsgBase, Authorization
* {@link DatasetGraphAccessControl} or because it has the context settings.
*/
public static boolean isAccessControlled(DatasetGraph dsg) {
if ( dsg == null )
return false;
if ( dsg instanceof DatasetGraphAccessControl )
return true;
// if ( dsg.getContext().isDefined(DataAccessCtl.symControlledAccess) )
// return true;
if ( dsg.getContext().isDefined(DataAccessCtl.symAuthorizationService) )
if ( dsg.getContext() != null && dsg.getContext().isDefined(DataAccessCtl.symAuthorizationService) )
return true;
return false;
}
Expand Down
6 changes: 0 additions & 6 deletions jena-fuseki2/jena-fuseki-core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,6 @@
<version>6.3.0-SNAPSHOT</version>
</dependency>

<dependency>
<groupId>org.apache.jena</groupId>
<artifactId>jena-shex</artifactId>
<version>6.3.0-SNAPSHOT</version>
</dependency>

<dependency>
<groupId>org.apache.jena</groupId>
<artifactId>jena-tdb1</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -203,13 +203,14 @@ public static boolean splitContains(String[] elts, String str) {
/**
* Parse RDF content from the body of the request of the action, ends the
* request, and sends a 400 if there is a parse error.
* Parse errors are logged as "fatal" and become 400/{@link ActionErrorException}
*
* @throws ActionErrorException ActionErrorException
*/
public static void parseOrError(HttpAction action, StreamRDF dest, Lang lang, String base) {
try {
parse(action, dest, lang, base);
} catch (RiotParseException ex) {
} catch (RiotException ex) {
ActionLib.consumeBody(action);
ServletOps.errorParseError(ex);
}
Expand All @@ -218,7 +219,7 @@ public static void parseOrError(HttpAction action, StreamRDF dest, Lang lang, St
/**
* Parse RDF content. This wraps up the parse step reading from an action.
* It includes handling compression if the {@code Content-Encoding} header is present
* @throws RiotParseException RiotParseException
* @throws RiotException
*/
public static void parse(HttpAction action, StreamRDF dest, Lang lang, String base) {
try {
Expand All @@ -229,7 +230,7 @@ public static void parse(HttpAction action, StreamRDF dest, Lang lang, String ba

/**
* Parse RDF content. This wraps up the parse step reading from an input stream.
* @throws RiotParseException RiotParseException
* @throws RiotException RiotException
*/
public static void parse(HttpAction action, StreamRDF dest, InputStream input, Lang lang, String base) {
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,13 @@
import org.apache.jena.graph.NodeFactory;
import org.apache.jena.riot.Lang;
import org.apache.jena.riot.RDFLanguages;
import org.apache.jena.riot.RiotException;
import org.apache.jena.riot.web.HttpNames;
import org.apache.jena.shacl.ShaclValidator;
import org.apache.jena.shacl.Shapes;
import org.apache.jena.shacl.ValidationReport;
import org.apache.jena.system.G;
import org.apache.jena.vocabulary.OWL1;
import org.apache.jena.web.HttpSC;

/**
Expand All @@ -48,6 +51,8 @@
*/
public class SHACL_Validation extends BaseActionREST { //ActionREST {

private static final Node owlImports = OWL1.imports.asNode();

public SHACL_Validation() {}

@Override
Expand All @@ -60,20 +65,39 @@ protected void doPost(HttpAction action) {

String targetNodeStr = action.getRequestParameter(HttpNames.paramTarget);

Graph shapesGraph;
try {
shapesGraph = ActionLib.readFromRequest(action, Lang.TTL);
if ( G.contains(shapesGraph, null, owlImports, null) ) {
action.log.error(format("[%d] shacl: owl:imports not supported for remote validation", action.id));
// Does not return.
ServletOps.errorBadRequest("owl:imports not allowed");
}
} catch (RiotException ex) {
shapesGraph = null;
// Does not return.
ServletOps.errorBadRequest(ex.getMessage());
}

action.beginRead();
try {
GraphTarget graphTarget = determineTarget(action.getActiveDSG(), action);
if ( ! graphTarget.exists() )
if ( ! graphTarget.exists() ) {
action.log.error(format("[%d] shacl: No data graph", action.id));
// Does not return.
ServletOps.errorNotFound("No data graph: "+graphTarget.label());
Graph data = graphTarget.graph();
Graph shapesGraph = ActionLib.readFromRequest(action, Lang.TTL);
}

Graph data = graphTarget.graph();
Node targetNode = null;
if ( targetNodeStr != null ) {
String x = data.getPrefixMapping().expandPrefix(targetNodeStr);
targetNode = NodeFactory.createURI(x);
}

// This does not resolve owl:imports.
// Doing so would lead to SSRF (server-side request forgery)
// with the server making a URL access on the users behalf.
Shapes shapes = Shapes.parse(shapesGraph);
ValidationReport report = ( targetNode == null )
? ShaclValidator.get().validate(shapesGraph, data)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@

import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.http.HttpServletResponse;

import org.apache.jena.atlas.RuntimeIOException;
import org.apache.jena.atlas.io.IO;
import org.apache.jena.atlas.json.JSON;
Expand All @@ -40,7 +39,7 @@
import org.apache.jena.fuseki.system.ConNeg;
import org.apache.jena.fuseki.system.UploadDetails;
import org.apache.jena.fuseki.system.UploadDetails.PreState;
import org.apache.jena.riot.RiotParseException;
import org.apache.jena.riot.RiotException;
import org.apache.jena.riot.WebContent;
import org.apache.jena.riot.web.HttpNames;
import org.apache.jena.web.HttpSC;
Expand Down Expand Up @@ -211,7 +210,7 @@ public static void warning(HttpAction action, String string, Throwable thorwable
action.log.warn(string, thorwable);
}

public static void errorParseError(RiotParseException ex) {
public static void errorParseError(RiotException ex) {
error(HttpSC.BAD_REQUEST_400, "Parse Error: "+ex.getMessage());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,12 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;

import org.junit.jupiter.api.*;
import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;

import org.apache.jena.atlas.logging.LogCtl;
import org.apache.jena.fuseki.Fuseki;
import org.apache.jena.graph.Graph;
import org.apache.jena.http.HttpRDF;
import org.apache.jena.rdfconnection.RDFConnection;
Expand Down Expand Up @@ -184,6 +188,17 @@ public void shacl_no_data_graph() {
});
}

@Test
public void shacl_imports() {
LogCtl.withLevel(Fuseki.actionLog, "FATAL", ()->{
withServer((datasetURL)->{
FusekiTestLib.expect400(()->{
validateReport(datasetURL+"/shacl?default", DIR+"shapes-imports.ttl");
});
});
});
}

private static ValidationReport validateReport(String url, String shapesFile) {
Graph shapesGraph = RDFDataMgr.loadGraph(shapesFile);
Graph responseGraph = HttpRDF.httpPostGraphRtn(url, shapesGraph);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
PREFIX owl: <http://www.w3.org/2002/07/owl#>
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
PREFIX sh: <http://www.w3.org/ns/shacl#>

PREFIX : <urn:sh:ex:>
PREFIX ex: <http://example/>
PREFIX ns: <http://example/ns#>

[] rdf:type owl:Ontology;
owl:imports <.> .

:nodeShape2 a sh:NodeShape ;
sh:targetSubjectsOf ns:p ;
sh:property [
sh:path ns:p;
sh:datatype xsd:string;
sh:maxCount 1 ;
sh:minCount 1 ;
] .