Skip to content

@mbaluda TEST: cds-ts-xml #134

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Jul 31, 2024
Merged
Show file tree
Hide file tree
Changes from 6 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
16 changes: 16 additions & 0 deletions .github/workflows/code_scanning.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,22 @@ jobs:
-o "$cds_file.json"
done

- name: Install tree-sitter-cli locally
run: |
npm i tree-sitter-cli @cap-js-community/tree-sitter-cds

# Parse .cds files to .cds.ts.xml files.
# for cds_file in $(find . -type f \( -iname "*.cds" -o -iname "*.htm" -o -iname "*.html" -o -iname "*.js" -o -iname "*.xml" -o -iname "*.json" \) -not -iname "*.ts.xml" )
- name: Parse CAP CDS files
run: |
for cds_file in $(find . -type f -iname "*.cds")
do
echo "I am parsing $cds_file to xml"
node_modules/tree-sitter-cli/tree-sitter parse --scope source.cds -x \
--config-path .github/workflows/tree-sitter-config.json $cds_file \
> "$cds_file.ts.xml"
done

- name: Extract CodeQL bundle version from qlt.conf.json
run: |
echo "BUNDLE_VERSION=$(jq .CodeQLCLIBundle qlt.conf.json -r)" >> $GITHUB_ENV
Expand Down
16 changes: 16 additions & 0 deletions .github/workflows/run-codeql-unit-tests-javascript.yml
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,22 @@ jobs:
-o "$cds_file.json"
done

- name: Install tree-sitter-cli locally
run: |
npm i tree-sitter-cli @cap-js-community/tree-sitter-cds

# Parse .cds files to .cds.ts.xml files.
# for cds_file in $(find . -type f \( -iname "*.cds" -o -iname "*.htm" -o -iname "*.html" -o -iname "*.js" -o -iname "*.xml" -o -iname "*.json" \) -not -iname "*.ts.xml" )
- name: Parse CAP CDS files
run: |
for cds_file in $(find . -type f -iname "*.cds")
do
echo "I am parsing $cds_file to xml"
node_modules/tree-sitter-cli/tree-sitter parse --scope source.cds -x \
--config-path .github/workflows/tree-sitter-config.json $cds_file \
> "$cds_file.ts.xml"
done

- name: Run test suites
id: run-test-suites
env:
Expand Down
5 changes: 5 additions & 0 deletions .github/workflows/tree-sitter-config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"parser-directories": [
"node_modules/@cap-js-community"
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import javascript

class TreeSitterXmlElement extends XmlElement {
TreeSitterXmlElement() { this.getFile().getName().matches("%.ts.xml") }

string getURL() {
result =
"file://" + this.getFile().getName().splitAt(".ts.xml") + ":" +
(this.getAttributeValue("srow").toInt() + 1) + ":" +
(this.getAttributeValue("scol").toInt() + 1) + ":" +
(this.getAttributeValue("erow").toInt() + 1) + ":" + this.getAttributeValue("ecol").toInt()
}
}

class CdsAnnotateElement extends TreeSitterXmlElement {
CdsAnnotateElement() { this.hasName("annotate_element") }

CdsAnnotation getAnnotation() { result = this.getAChild() }

CdsIdentifier getIdentifier() { result = this.getAChild() }
}

class CdsAnnotation extends TreeSitterXmlElement {
CdsAnnotation() { this.hasName("annotation") }

CdsAnnotationPath getAnnotationPath() { result = this.getAChild() }
}

class CdsAnnotationPath extends TreeSitterXmlElement {
CdsAnnotationPath() { this.hasName("annotation_path") }

CdsIdentifier getIdentifier() { result = this.getAChild() }
}

class CdsIdentifier extends TreeSitterXmlElement {
CdsIdentifier() { this.hasName("identifier") }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# CAP Insertion of Sensitive Information into Log File

If sensitive information is written to a log entry using the CAP Node.js logging API, a malicious user may be able to gain access to user data.

Data annotated as `@PersonalData` should not be logged.

## Recommendation

CAP applications should not log sensitive information. Check CDS declarations for annotations before logging certain data types or fields.

## Examples

This CAP service directly logs the sensitive information.

```cds
namespace advanced_security.log_exposure.sample_entities;

entity Sample {
name : String(111);
}

// annotations for Data Privacy
annotate Sample with
@PersonalData : { DataSubjectRole : 'Sample', EntitySemantics : 'DataSubject' }
{
name @PersonalData.IsPotentiallySensitive;
}
```

``` javascript
import cds from '@sap/cds'
const LOG = cds.log("logger");

const { Sample } = cds.entities('advanced_security.log_exposure.sample_entities')

class SampleVulnService extends cds.ApplicationService {
init() {
LOG.info("Received: ", Sample.name); // CAP log exposure alert
}
}
```

## References

- OWASP 2021: [Security Logging and Monitoring Failures](https://owasp.org/Top10/A09_2021-Security_Logging_and_Monitoring_Failures/).
- OWASP: [Logging Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Logging_Cheat_Sheet.html).
- OWASP: [User Privacy Protection Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/User_Privacy_Protection_Cheat_Sheet.html).
- SAP CAPire Documentation: [PersonalData Annotations](https://cap.cloud.sap/docs/guides/data-privacy/annotations).
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/**
* @name Insertion of sensitive information into log files
* @description Writing sensitive information to log files can allow that
* information to be leaked to an attacker more easily.
* @kind path-problem
* @problem.severity warning
* @security-severity 7.5
* @precision medium
* @id javascript/sensitive-log-cds
* @tags security
* external/cwe/cwe-532
*/

import javascript
import advanced_security.javascript.frameworks.cap.CDS
import advanced_security.javascript.frameworks.cap.CAPLogInjectionQuery
import DataFlow::PathGraph
import CdsTreeSitterXml

class SensitiveExposureSource extends DataFlow::Node {
SensitiveExposureSource() {
exists(PropRead p, SensitiveAnnotatedElement c |
p.getPropertyName() = c.getName() and
this = p
)
}
}

class SensitiveLogExposureConfig extends TaintTracking::Configuration {
SensitiveLogExposureConfig() { this = "SensitiveLogExposure" }

override predicate isSource(DataFlow::Node source) { source instanceof SensitiveExposureSource }

override predicate isSink(DataFlow::Node sink) { sink instanceof CdsLogSink }
}

CdsAnnotateElement getSensitiveAnnotation(PropRead s) {
result.getAnnotation().getAnnotationPath().getIdentifier().getTextValue() = "PersonalData" and
result.getIdentifier().getTextValue() = s.getPropertyName()
}

from
SensitiveLogExposureConfig config, DataFlow::PathNode source, DataFlow::PathNode sink,
TreeSitterXmlElement annotation
where
config.hasFlowPath(source, sink) and
annotation = getSensitiveAnnotation(source.getNode())
select sink, source, sink, "Log entry depends on a $@ piece of information.", annotation,
"potentially sensitive"
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
namespace advanced_security.log_exposure.sample_entities;

entity Sample {
name : String(111);
dateOfBirth : Date;
}

// annotations for Data Privacy
annotate Sample with
@PersonalData : { DataSubjectRole : 'Sample', EntitySemantics : 'DataSubject' }
{
name @PersonalData.IsPotentiallySensitive;
dateOfBirth @PersonalData.IsPotentiallyPersonal;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
nodes
| sensitive-exposure.js:10:32:10:42 | Sample.name |
| sensitive-exposure.js:10:32:10:42 | Sample.name |
| sensitive-exposure.js:10:32:10:42 | Sample.name |
edges
| sensitive-exposure.js:10:32:10:42 | Sample.name | sensitive-exposure.js:10:32:10:42 | Sample.name |
#select
| sensitive-exposure.js:10:32:10:42 | Sample.name | sensitive-exposure.js:10:32:10:42 | Sample.name | sensitive-exposure.js:10:32:10:42 | Sample.name | Log entry depends on a $@ piece of information. | sensitive-exposure.cds:12:3:12:45 | annotate_element | potentially sensitive |
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import cds from '@sap/cds'
const LOG = cds.log("logger");

const { Sample } = cds.entities('advanced_security.log_exposure.sample_entities')

class SampleVulnService extends cds.ApplicationService {
init() {
/* A sensitive info log sink. */

LOG.info("Received: ", Sample.name); // CAP log exposure alert

Check failure

Code scanning / CodeQL

Insertion of sensitive information into log files High test

Log entry depends on a
potentially sensitive
piece of information.

Check failure

Code scanning / CodeQL

Insertion of sensitive information into log files High test

Log entry depends on a potentially sensitive piece of information.
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
sensitive-exposure/SensitiveExposureCds.ql
Loading