diff --git a/javascript/frameworks/cap/lib/advanced_security/javascript/frameworks/cap/CDL.qll b/javascript/frameworks/cap/lib/advanced_security/javascript/frameworks/cap/CDL.qll index 934371008..f4811c9ff 100644 --- a/javascript/frameworks/cap/lib/advanced_security/javascript/frameworks/cap/CDL.qll +++ b/javascript/frameworks/cap/lib/advanced_security/javascript/frameworks/cap/CDL.qll @@ -7,20 +7,58 @@ import advanced_security.javascript.frameworks.cap.CDS abstract class CdlObject extends JsonObject { predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) { - exists(Location loc, JsonValue locValue | - loc = this.getLocation() and - locValue = this.getPropValue("$location") and - path = - any(File f | - f.getAbsolutePath() - .matches("%" + locValue.getPropValue("file").getStringValue() + ".json") - ).getAbsolutePath().regexpReplaceAll("\\.json$", "") and - sl = locValue.getPropValue("line").getIntValue() and - sc = locValue.getPropValue("col").getIntValue() and - el = sl + 1 and - ec = 1 - ) + // If the cds.json file has a $location property, then use that, + // otherwise fall back to the cds.json file itself + if exists(this.getPropValue("$location")) + then + exists(Location loc, JsonValue locValue | + loc = this.getLocation() and + locValue = this.getPropValue("$location") and + path = + any(File f | + f.getAbsolutePath() + .matches("%" + locValue.getPropValue("file").getStringValue() + ".json") + ).getAbsolutePath().regexpReplaceAll("\\.json$", "") and + if + not exists(locValue.getPropValue("line")) and + not exists(locValue.getPropValue("col")) + then + // We don't know where this entity starts, so mark the whole file + sl = 0 and + sc = 0 and + el = 0 and + ec = 0 + else ( + sl = locValue.getPropValue("line").getIntValue() and + ( + if exists(locValue.getPropValue("col")) + then sc = locValue.getPropValue("col").getIntValue() + else + // We don't know where this entity starts, so mark the start of the line + sc = 0 + ) and + el = sl and + ( + if exists(getObjectLocationName()) + then + // Currently $locations does not provide an end location. However, we can + // automatically deduce the end location from the length of the name. + ec = sc + getObjectLocationName().length() - 1 + else + // Mark a single character if we cannot predicate the length + ec = sc + 1 + ) + ) + ) + else super.getLocation().hasLocationInfo(path, sl, sc, el, ec) } + + /** + * The name of the object that should be highlighted as the location. + * + * This is used to deduce the length of the location. + */ + string getObjectLocationName() { none() } } private newtype CdlKind = @@ -31,21 +69,26 @@ private newtype CdlKind = CdlFunctionKind(string value) { value = "function" } /** - * Any CDL element, including entities, event, actions, and more. + * A list of CDL definitions, which can include entities, events, actions and more. */ -class CdlDefinition extends CdlObject { - CdlDefinition() { exists(JsonObject root | this = root.getPropValue("definitions")) } +class CdlDefinitions extends CdlObject { + CdlDefinitions() { exists(JsonObject root | this = root.getPropValue("definitions")) } JsonObject getElement(string elementName) { result = this.getPropValue(elementName) } JsonObject getAnElement() { result = this.getElement(_) } } +/** + * A CDL definition element. + */ abstract class CdlElement extends CdlObject { CdlKind kind; string name; - CdlElement() { exists(CdlDefinition definition | this = definition.getElement(name)) } + CdlElement() { exists(CdlDefinitions definitions | this = definitions.getElement(name)) } + + override string getObjectLocationName() { result = getUnqualifiedName() } /** * Gets the name of this CDL element. @@ -215,6 +258,8 @@ class CdlAttribute extends CdlObject { exists(CdlElement entity | this = entity.getPropValue("elements").getPropValue(name)) } + override string getObjectLocationName() { result = getName() } + string getType() { result = this.getPropStringValue("type") } int getLength() { result = this.getPropValue("length").(JsonPrimitiveValue).getIntValue() } diff --git a/javascript/frameworks/cap/test/queries/bad-authn-authz/entities-with-no-authz/entities-exposed-with-no-authz/entities-exposed-with-no-authz.expected b/javascript/frameworks/cap/test/queries/bad-authn-authz/entities-with-no-authz/entities-exposed-with-no-authz/entities-exposed-with-no-authz.expected index 0a226b09d..b1b701a87 100644 --- a/javascript/frameworks/cap/test/queries/bad-authn-authz/entities-with-no-authz/entities-exposed-with-no-authz/entities-exposed-with-no-authz.expected +++ b/javascript/frameworks/cap/test/queries/bad-authn-authz/entities-with-no-authz/entities-exposed-with-no-authz/entities-exposed-with-no-authz.expected @@ -1,6 +1,6 @@ -| srv/service1.cds:3:9:4:1 | {\\n ... }\\n } | The CDS service `Service1` is exposed without any authentication. | -| srv/service1.cds:5:10:6:1 | {\\n ... }\\n } | The CDS entity `Service1.Service1Entity` is exposed without any authentication. | -| srv/service1.cds:8:10:9:1 | {\\n ... }\\n } | The CDS action `Service1.send1` is exposed without any authentication. | -| srv/service2.cds:3:9:4:1 | {\\n ... }\\n } | The CDS service `Service2` is exposed without any authentication. | -| srv/service2.cds:5:10:6:1 | {\\n ... }\\n } | The CDS entity `Service2.Service2Entity` is exposed without any authentication. | -| srv/service2.cds:8:10:9:1 | {\\n ... }\\n } | The CDS action `Service2.send2` is exposed without any authentication. | +| srv/service1.cds:3:9:3:16 | {\\n ... }\\n } | The CDS service `Service1` is exposed without any authentication. | +| srv/service1.cds:5:10:5:23 | {\\n ... }\\n } | The CDS entity `Service1.Service1Entity` is exposed without any authentication. | +| srv/service1.cds:8:10:8:14 | {\\n ... }\\n } | The CDS action `Service1.send1` is exposed without any authentication. | +| srv/service2.cds:3:9:3:16 | {\\n ... }\\n } | The CDS service `Service2` is exposed without any authentication. | +| srv/service2.cds:5:10:5:23 | {\\n ... }\\n } | The CDS entity `Service2.Service2Entity` is exposed without any authentication. | +| srv/service2.cds:8:10:8:14 | {\\n ... }\\n } | The CDS action `Service2.send2` is exposed without any authentication. | \ No newline at end of file diff --git a/javascript/frameworks/cap/test/queries/sensitive-exposure/sensitive-exposure.expected b/javascript/frameworks/cap/test/queries/sensitive-exposure/sensitive-exposure.expected index c1efa56fc..85d27ac4f 100644 --- a/javascript/frameworks/cap/test/queries/sensitive-exposure/sensitive-exposure.expected +++ b/javascript/frameworks/cap/test/queries/sensitive-exposure/sensitive-exposure.expected @@ -5,4 +5,4 @@ nodes edges | sensitive-exposure.js:9:32:9:42 | Sample.name | sensitive-exposure.js:9:32:9:42 | Sample.name | #select -| sensitive-exposure.js:9:32:9:42 | Sample.name | sensitive-exposure.js:9:32:9:42 | Sample.name | sensitive-exposure.js:9:32:9:42 | Sample.name | Log entry depends on the $@ field which is annotated as potentially sensitive. | sensitive-exposure.cds:4:5:5:1 | {\\n ... } | name | +| sensitive-exposure.js:9:32:9:42 | Sample.name | sensitive-exposure.js:9:32:9:42 | Sample.name | sensitive-exposure.js:9:32:9:42 | Sample.name | Log entry depends on the $@ field which is annotated as potentially sensitive. | sensitive-exposure.cds:4:5:4:8 | {\\n ... } | name | \ No newline at end of file