Skip to content

C#: Support suppression comments in XML files #4948

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

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 2 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
61 changes: 61 additions & 0 deletions cpp/ql/src/semmle/code/cpp/XML.qll
Original file line number Diff line number Diff line change
Expand Up @@ -341,3 +341,64 @@ class XMLCharacters extends @xmlcharacters, XMLLocatable {
/** Gets a printable representation of this XML character sequence. */
override string toString() { result = this.getCharacters() }
}


/**
* An alert suppression comment.
*/
class XMLSuppressionComment extends XMLComment {
string annotation;

XMLSuppressionComment() {
// suppression comments must be single-line
not getText().matches("%\n%") and
(
// match `lgtm[...]` anywhere in the comment
annotation = getText().regexpFind("(?i)\\blgtm\\s*\\[[^\\]]*\\]", _, _)
or
// match `lgtm` at the start of the comment and after semicolon
annotation = getText().regexpFind("(?i)(?<=^|;)\\s*lgtm(?!\\B|\\s*\\[)", _, _).trim()
)
}

/** Gets the suppression annotation in this comment. */
string getAnnotation() { result = annotation }

/**
* Holds if this comment applies to the range from column `startcolumn` of line `startline`
* to column `endcolumn` of line `endline` in file `filepath`.
*/
predicate covers(string filepath, int startline, int startcolumn, int endline, int endcolumn) {
this.getLocation().hasLocationInfo(filepath, startline, _, endline, endcolumn) and
startcolumn = 1
}

/** Gets the scope of this suppression. */
XMLSuppressionScope getScope() { this = result.getSuppressionComment() }
}

/**
* The scope of an alert suppression comment.
*/
class XMLSuppressionScope extends @xmlcomment {
XMLSuppressionScope() { this instanceof XMLSuppressionComment }

/** Gets a suppression comment with this scope. */
XMLSuppressionComment getSuppressionComment() { result = this }

/**
* Holds if this element is at the specified location.
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
this.(XMLSuppressionComment).covers(filepath, startline, startcolumn, endline, endcolumn)
}

/** Gets a textual representation of this element. */
string toString() { result = "suppression range" }
}
14 changes: 14 additions & 0 deletions csharp/ql/src/XMLAlertSuppression.ql
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/**
* @name XML alert suppression
* @description Generates information about alert suppressions in XML files.
* @kind alert-suppression
* @id cs/xml-alert-suppression
*/

import semmle.code.csharp.XML

from XMLSuppressionComment c
select c, // suppression comment
c.getText(), // text of suppression comment (excluding delimiters)
c.getAnnotation(), // text of suppression annotation
c.getScope() // scope of suppression
61 changes: 61 additions & 0 deletions csharp/ql/src/semmle/code/csharp/XML.qll
Original file line number Diff line number Diff line change
Expand Up @@ -341,3 +341,64 @@ class XMLCharacters extends @xmlcharacters, XMLLocatable {
/** Gets a printable representation of this XML character sequence. */
override string toString() { result = this.getCharacters() }
}


/**
* An alert suppression comment.
*/
class XMLSuppressionComment extends XMLComment {
string annotation;

XMLSuppressionComment() {
// suppression comments must be single-line
not getText().matches("%\n%") and
(
// match `lgtm[...]` anywhere in the comment
annotation = getText().regexpFind("(?i)\\blgtm\\s*\\[[^\\]]*\\]", _, _)
or
// match `lgtm` at the start of the comment and after semicolon
annotation = getText().regexpFind("(?i)(?<=^|;)\\s*lgtm(?!\\B|\\s*\\[)", _, _).trim()
)
}

/** Gets the suppression annotation in this comment. */
string getAnnotation() { result = annotation }

/**
* Holds if this comment applies to the range from column `startcolumn` of line `startline`
* to column `endcolumn` of line `endline` in file `filepath`.
*/
predicate covers(string filepath, int startline, int startcolumn, int endline, int endcolumn) {
this.getLocation().hasLocationInfo(filepath, startline, _, endline, endcolumn) and
startcolumn = 1
}

/** Gets the scope of this suppression. */
XMLSuppressionScope getScope() { this = result.getSuppressionComment() }
}

/**
* The scope of an alert suppression comment.
*/
class XMLSuppressionScope extends @xmlcomment {
XMLSuppressionScope() { this instanceof XMLSuppressionComment }

/** Gets a suppression comment with this scope. */
XMLSuppressionComment getSuppressionComment() { result = this }

/**
* Holds if this element is at the specified location.
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
this.(XMLSuppressionComment).covers(filepath, startline, startcolumn, endline, endcolumn)
}

/** Gets a textual representation of this element. */
string toString() { result = "suppression range" }
}
27 changes: 27 additions & 0 deletions csharp/ql/test/query-tests/AlertSuppression/Web.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<!-- lgtm -->
<!-- lgtm[cs/unused-reftype] -->
<!-- lgtm[cs/unused-reftype, cs/unused-field] -->
<!-- lgtm[@tag:nullness] -->
<!-- lgtm[@tag:useless-code,cs/unused-reftype] -->
<!-- lgtm[@expires:2017-06-11] -->
<!-- lgtm[cs/unused-reftype] because I know better than lgtm -->
<!-- lgtm: blah blah -->
<!-- lgtm blah blah #falsepositive -->
<!--lgtm [cs/unused-reftype]-->
<!-- lgtm[] -->
<!-- lgtmfoo -->
<!--lgtm-->
<!-- lgtm -->
<!-- lgtm [cs/unused-reftype] -->
<!-- foolgtm[cs/unused-reftype] -->
<!-- foolgtm -->
<!-- foo; lgtm -->
<!-- foo; lgtm[cs/unused-reftype] -->
<!-- foo lgtm -->
<!-- foo lgtm[cs/unused-reftype] -->
<!-- foo lgtm bar -->
<!-- foo lgtm[cs/unused-reftype] bar -->
<!-- LGTM! -->
<!-- LGTM[cs/unused-reftype] -->
<!-- lgtm[cs/unused-reftype] and lgtm[cs/unused-field] -->
<!-- lgtm[cs/unused-reftype]; lgtm -->
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
| Web.config:1:1:2:1 | lgtm | lgtm | lgtm | Web.config:1:1:2:1 | suppression range |
| Web.config:2:1:3:1 | lgtm[cs/unused-reftype] | lgtm[cs/unused-reftype] | lgtm[cs/unused-reftype] | Web.config:2:1:3:1 | suppression range |
| Web.config:3:1:4:1 | lgtm[cs/unused-reftype, cs/unused-field] | lgtm[cs/unused-reftype, cs/unused-field] | lgtm[cs/unused-reftype, cs/unused-field] | Web.config:3:1:4:1 | suppression range |
| Web.config:4:1:5:1 | lgtm[@tag:nullness] | lgtm[@tag:nullness] | lgtm[@tag:nullness] | Web.config:4:1:5:1 | suppression range |
| Web.config:5:1:6:1 | lgtm[@tag:useless-code,cs/unused-reftype] | lgtm[@tag:useless-code,cs/unused-reftype] | lgtm[@tag:useless-code,cs/unused-reftype] | Web.config:5:1:6:1 | suppression range |
| Web.config:6:1:7:1 | lgtm[@expires:2017-06-11] | lgtm[@expires:2017-06-11] | lgtm[@expires:2017-06-11] | Web.config:6:1:7:1 | suppression range |
| Web.config:7:1:8:1 | lgtm[cs/unused-reftype] because I know better than lgtm | lgtm[cs/unused-reftype] because I know better than lgtm | lgtm[cs/unused-reftype] | Web.config:7:1:8:1 | suppression range |
| Web.config:8:1:9:1 | lgtm: blah blah | lgtm: blah blah | lgtm | Web.config:8:1:9:1 | suppression range |
| Web.config:9:1:10:1 | lgtm blah blah #falsepositive | lgtm blah blah #falsepositive | lgtm | Web.config:9:1:10:1 | suppression range |
| Web.config:10:1:11:1 | lgtm [cs/unused-reftype] | lgtm [cs/unused-reftype] | lgtm [cs/unused-reftype] | Web.config:10:1:11:1 | suppression range |
| Web.config:11:1:12:1 | lgtm[] | lgtm[] | lgtm[] | Web.config:11:1:12:1 | suppression range |
| Web.config:13:1:14:1 | lgtm | lgtm | lgtm | Web.config:13:1:14:1 | suppression range |
| Web.config:14:1:15:1 | lgtm | lgtm | lgtm | Web.config:14:1:15:1 | suppression range |
| Web.config:15:1:16:1 | lgtm [cs/unused-reftype] | lgtm [cs/unused-reftype] | lgtm [cs/unused-reftype] | Web.config:15:1:16:1 | suppression range |
| Web.config:18:1:19:1 | foo; lgtm | foo; lgtm | lgtm | Web.config:18:1:19:1 | suppression range |
| Web.config:19:1:20:1 | foo; lgtm[cs/unused-reftype] | foo; lgtm[cs/unused-reftype] | lgtm[cs/unused-reftype] | Web.config:19:1:20:1 | suppression range |
| Web.config:21:1:22:1 | foo lgtm[cs/unused-reftype] | foo lgtm[cs/unused-reftype] | lgtm[cs/unused-reftype] | Web.config:21:1:22:1 | suppression range |
| Web.config:23:1:24:1 | foo lgtm[cs/unused-reftype] bar | foo lgtm[cs/unused-reftype] bar | lgtm[cs/unused-reftype] | Web.config:23:1:24:1 | suppression range |
| Web.config:24:1:25:1 | LGTM! | LGTM! | LGTM | Web.config:24:1:25:1 | suppression range |
| Web.config:25:1:26:1 | LGTM[cs/unused-reftype] | LGTM[cs/unused-reftype] | LGTM[cs/unused-reftype] | Web.config:25:1:26:1 | suppression range |
| Web.config:26:1:27:1 | lgtm[cs/unused-reftype] and lgtm[cs/unused-field] | lgtm[cs/unused-reftype] and lgtm[cs/unused-field] | lgtm[cs/unused-field] | Web.config:26:1:27:1 | suppression range |
| Web.config:26:1:27:1 | lgtm[cs/unused-reftype] and lgtm[cs/unused-field] | lgtm[cs/unused-reftype] and lgtm[cs/unused-field] | lgtm[cs/unused-reftype] | Web.config:26:1:27:1 | suppression range |
| Web.config:27:1:27:39 | lgtm[cs/unused-reftype]; lgtm | lgtm[cs/unused-reftype]; lgtm | lgtm | Web.config:27:1:27:39 | suppression range |
| Web.config:27:1:27:39 | lgtm[cs/unused-reftype]; lgtm | lgtm[cs/unused-reftype]; lgtm | lgtm[cs/unused-reftype] | Web.config:27:1:27:39 | suppression range |
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
XMLAlertSuppression.ql
61 changes: 61 additions & 0 deletions java/ql/src/semmle/code/xml/XML.qll
Original file line number Diff line number Diff line change
Expand Up @@ -341,3 +341,64 @@ class XMLCharacters extends @xmlcharacters, XMLLocatable {
/** Gets a printable representation of this XML character sequence. */
override string toString() { result = this.getCharacters() }
}


/**
* An alert suppression comment.
*/
class XMLSuppressionComment extends XMLComment {
string annotation;

XMLSuppressionComment() {
// suppression comments must be single-line
not getText().matches("%\n%") and
(
// match `lgtm[...]` anywhere in the comment
annotation = getText().regexpFind("(?i)\\blgtm\\s*\\[[^\\]]*\\]", _, _)
or
// match `lgtm` at the start of the comment and after semicolon
annotation = getText().regexpFind("(?i)(?<=^|;)\\s*lgtm(?!\\B|\\s*\\[)", _, _).trim()
)
}

/** Gets the suppression annotation in this comment. */
string getAnnotation() { result = annotation }

/**
* Holds if this comment applies to the range from column `startcolumn` of line `startline`
* to column `endcolumn` of line `endline` in file `filepath`.
*/
predicate covers(string filepath, int startline, int startcolumn, int endline, int endcolumn) {
this.getLocation().hasLocationInfo(filepath, startline, _, endline, endcolumn) and
startcolumn = 1
}

/** Gets the scope of this suppression. */
XMLSuppressionScope getScope() { this = result.getSuppressionComment() }
}

/**
* The scope of an alert suppression comment.
*/
class XMLSuppressionScope extends @xmlcomment {
XMLSuppressionScope() { this instanceof XMLSuppressionComment }

/** Gets a suppression comment with this scope. */
XMLSuppressionComment getSuppressionComment() { result = this }

/**
* Holds if this element is at the specified location.
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
this.(XMLSuppressionComment).covers(filepath, startline, startcolumn, endline, endcolumn)
}

/** Gets a textual representation of this element. */
string toString() { result = "suppression range" }
}
61 changes: 61 additions & 0 deletions javascript/ql/src/semmle/javascript/XML.qll
Original file line number Diff line number Diff line change
Expand Up @@ -341,3 +341,64 @@ class XMLCharacters extends @xmlcharacters, XMLLocatable {
/** Gets a printable representation of this XML character sequence. */
override string toString() { result = this.getCharacters() }
}


/**
* An alert suppression comment.
*/
class XMLSuppressionComment extends XMLComment {
string annotation;

XMLSuppressionComment() {
// suppression comments must be single-line
not getText().matches("%\n%") and
(
// match `lgtm[...]` anywhere in the comment
annotation = getText().regexpFind("(?i)\\blgtm\\s*\\[[^\\]]*\\]", _, _)
or
// match `lgtm` at the start of the comment and after semicolon
annotation = getText().regexpFind("(?i)(?<=^|;)\\s*lgtm(?!\\B|\\s*\\[)", _, _).trim()
)
}

/** Gets the suppression annotation in this comment. */
string getAnnotation() { result = annotation }

/**
* Holds if this comment applies to the range from column `startcolumn` of line `startline`
* to column `endcolumn` of line `endline` in file `filepath`.
*/
predicate covers(string filepath, int startline, int startcolumn, int endline, int endcolumn) {
this.getLocation().hasLocationInfo(filepath, startline, _, endline, endcolumn) and
startcolumn = 1
}

/** Gets the scope of this suppression. */
XMLSuppressionScope getScope() { this = result.getSuppressionComment() }
}

/**
* The scope of an alert suppression comment.
*/
class XMLSuppressionScope extends @xmlcomment {
XMLSuppressionScope() { this instanceof XMLSuppressionComment }

/** Gets a suppression comment with this scope. */
XMLSuppressionComment getSuppressionComment() { result = this }

/**
* Holds if this element is at the specified location.
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
this.(XMLSuppressionComment).covers(filepath, startline, startcolumn, endline, endcolumn)
}

/** Gets a textual representation of this element. */
string toString() { result = "suppression range" }
}
61 changes: 61 additions & 0 deletions python/ql/src/semmle/python/xml/XML.qll
Original file line number Diff line number Diff line change
Expand Up @@ -341,3 +341,64 @@ class XMLCharacters extends @xmlcharacters, XMLLocatable {
/** Gets a printable representation of this XML character sequence. */
override string toString() { result = this.getCharacters() }
}


/**
* An alert suppression comment.
*/
class XMLSuppressionComment extends XMLComment {
string annotation;

XMLSuppressionComment() {
// suppression comments must be single-line
not getText().matches("%\n%") and
(
// match `lgtm[...]` anywhere in the comment
annotation = getText().regexpFind("(?i)\\blgtm\\s*\\[[^\\]]*\\]", _, _)
or
// match `lgtm` at the start of the comment and after semicolon
annotation = getText().regexpFind("(?i)(?<=^|;)\\s*lgtm(?!\\B|\\s*\\[)", _, _).trim()
)
}

/** Gets the suppression annotation in this comment. */
string getAnnotation() { result = annotation }

/**
* Holds if this comment applies to the range from column `startcolumn` of line `startline`
* to column `endcolumn` of line `endline` in file `filepath`.
*/
predicate covers(string filepath, int startline, int startcolumn, int endline, int endcolumn) {
this.getLocation().hasLocationInfo(filepath, startline, _, endline, endcolumn) and
startcolumn = 1
}

/** Gets the scope of this suppression. */
XMLSuppressionScope getScope() { this = result.getSuppressionComment() }
}

/**
* The scope of an alert suppression comment.
*/
class XMLSuppressionScope extends @xmlcomment {
XMLSuppressionScope() { this instanceof XMLSuppressionComment }

/** Gets a suppression comment with this scope. */
XMLSuppressionComment getSuppressionComment() { result = this }

/**
* Holds if this element is at the specified location.
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
this.(XMLSuppressionComment).covers(filepath, startline, startcolumn, endline, endcolumn)
}

/** Gets a textual representation of this element. */
string toString() { result = "suppression range" }
}