-
Notifications
You must be signed in to change notification settings - Fork 1.7k
update the style guide on alert-messages #10405
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
Changes from 5 commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
338aead
add more guidance to the style-guide about alert messages
erik-krogh 93a6710
add a QL-for-QL query highlighting some issues with alert-texts
erik-krogh 59c1ac2
Apply suggestions from code review
erik-krogh 88f1d2a
add qldocs to the ql/alert-message-style-violation query
erik-krogh 8b3ba38
changes based on review
erik-krogh 9e56128
apply suggestions from doc review
erik-krogh e3990e8
add a line about link texts
erik-krogh abb5c38
move the guides on link-texts into a new subsection
erik-krogh File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,198 @@ | ||
/** | ||
* @name Alert message style violation | ||
* @description An alert message that doesn't follow the style guide is harder for end users to digest. | ||
* See the style guide here: https://github.com/github/codeql/blob/main/docs/query-metadata-style-guide.md#alert-messages | ||
kaeluka marked this conversation as resolved.
Show resolved
Hide resolved
|
||
* @kind problem | ||
* @problem.severity warning | ||
* @id ql/alert-message-style-violation | ||
* @precision high | ||
*/ | ||
|
||
import ql | ||
|
||
/** Gets the `index`th part of the select statement. */ | ||
private AstNode getSelectPart(Select sel, int index) { | ||
result = | ||
rank[index](AstNode n, Location loc | | ||
( | ||
n.getParent*() = sel.getExpr(_) and loc = n.getLocation() | ||
or | ||
// the strings are behind a predicate call. | ||
exists(Call c, Predicate target | | ||
c.getParent*() = sel.getExpr(_) and loc = c.getLocation() | ||
| | ||
c.getTarget() = target and | ||
( | ||
target.getBody().(ComparisonFormula).getAnOperand() = n | ||
or | ||
exists(ClassPredicate sub | sub.overrides(target) | | ||
sub.getBody().(ComparisonFormula).getAnOperand() = n | ||
) | ||
) | ||
) | ||
) | ||
| | ||
n | ||
order by | ||
loc.getStartLine(), loc.getStartColumn(), loc.getEndLine(), loc.getEndColumn(), | ||
loc.getFile().getRelativePath() | ||
) | ||
} | ||
|
||
/** | ||
* Gets a string element that is the last part of the message, that doesn't end with a full stop. | ||
erik-krogh marked this conversation as resolved.
Show resolved
Hide resolved
|
||
* | ||
* E.g. | ||
erik-krogh marked this conversation as resolved.
Show resolved
Hide resolved
|
||
* ```CodeQL | ||
* select foo(), "This is a description" // <- bad | ||
* | ||
* select foo(), "This is a description." // <- good | ||
* ``` | ||
*/ | ||
String shouldHaveFullStop(Select sel) { | ||
result = | ||
max(AstNode str, int i | | ||
str.getParent+() = sel.getExpr(1) and str = getSelectPart(sel, i) | ||
| | ||
str order by i | ||
) and | ||
not result.getValue().matches("%.") and | ||
not result.getValue().matches("%?") | ||
} | ||
|
||
/** | ||
* Gets a string element that is the first part of the message, that starts with a lower case letter. | ||
* | ||
* E.g. | ||
erik-krogh marked this conversation as resolved.
Show resolved
Hide resolved
|
||
* ```CodeQL | ||
* select foo(), "this is a description." // <- bad | ||
* | ||
* select foo(), "This is a description." // <- good | ||
* ``` | ||
*/ | ||
String shouldStartCapital(Select sel) { | ||
result = | ||
min(AstNode str, int i | | ||
str.getParent+() = sel.getExpr(1) and str = getSelectPart(sel, i) | ||
| | ||
str order by i | ||
) and | ||
result.getValue().regexpMatch("^[a-z].*") | ||
} | ||
|
||
/** | ||
* Gets a string element that is used in a message that contains "here" or "this location". | ||
* | ||
* E.g. | ||
erik-krogh marked this conversation as resolved.
Show resolved
Hide resolved
|
||
* ```CodeQL | ||
* select foo(), "XSS happens here from using a unsafe value." // <- bad | ||
* | ||
* select foo(), "XSS from using a unsafe value." // <- good | ||
* ``` | ||
*/ | ||
String avoidHere(string part) { | ||
part = ["here", "this location"] and | ||
( | ||
result.getValue().regexpMatch(".*\\b" + part + "\\b.*") and | ||
result = getSelectPart(_, _) | ||
) | ||
} | ||
|
||
/** | ||
* Avoid using an indefinite article ("a" or "an") in a link text. | ||
* | ||
* E.g. | ||
erik-krogh marked this conversation as resolved.
Show resolved
Hide resolved
|
||
* ```CodeQL | ||
* select foo(), "XSS from $@", val, "an unsafe value." // <- bad | ||
* | ||
* select foo(), "XSS from a $@", val, "unsafe value." // <- good | ||
* ``` | ||
* | ||
* See https://www.w3.org/WAI/WCAG22/Understanding/link-purpose-in-context.html for the W3C guideline on link text. a | ||
*/ | ||
String avoidArticleInLinkText(Select sel) { | ||
result = sel.getExpr((any(int i | i > 1))) and | ||
result = getSelectPart(sel, _) and | ||
result.getValue().regexpMatch("a|an .*") | ||
} | ||
|
||
/** | ||
* Don't quote substitutions in a message. | ||
* | ||
* E.g. | ||
erik-krogh marked this conversation as resolved.
Show resolved
Hide resolved
|
||
* ```CodeQL | ||
* select foo(), "XSS from '$@'", val, "an unsafe value." // <- bad | ||
* | ||
* select foo(), "XSS from $@", val, "an unsafe value." // <- good | ||
* ``` | ||
*/ | ||
String dontQuoteSubstitutions(Select sel) { | ||
result = getSelectPart(sel, _) and | ||
result.getValue().matches(["%'$@'%", "%\"$@\"%"]) | ||
} | ||
|
||
/** | ||
* Gets the kind of the path-query represented by `sel`. | ||
* Either "data" for a dataflow query or "taint" for a taint-tracking query. | ||
*/ | ||
private string getQueryKind(Select sel) { | ||
exists(TypeExpr sup | | ||
sup = sel.getVarDecl(_).getType().(ClassType).getDeclaration().getASuperType() and | ||
sup.getResolvedType().(ClassType).getName() = "Configuration" | ||
| | ||
result = "data" and | ||
sup.getModule().getName() = "DataFlow" | ||
or | ||
result = "taint" and | ||
sup.getModule().getName() = "TaintTracking" | ||
) | ||
} | ||
|
||
/** | ||
* Gets a string element from a message that uses the wrong phrase for a path query. | ||
* A dataflow query should use "flows to" and a taint-tracking query should use "depends on". | ||
*/ | ||
String wrongFlowsPhrase(Select sel, string kind) { | ||
result = getSelectPart(sel, _) and | ||
kind = getQueryKind(sel) and | ||
( | ||
kind = "data" and | ||
result.getValue().matches(["% depends %", "% depend %"]) | ||
or | ||
kind = "taint" and | ||
result.getValue().matches(["% flows to %", "% flow to %"]) | ||
) | ||
} | ||
|
||
from AstNode node, string msg | ||
where | ||
not node.getLocation().getFile().getAbsolutePath().matches("%/test/%") and | ||
( | ||
node = shouldHaveFullStop(_) and | ||
msg = "Alert message should end with a full stop." | ||
kaeluka marked this conversation as resolved.
Show resolved
Hide resolved
|
||
or | ||
node = shouldStartCapital(_) and | ||
msg = "Alert message should start with a capital letter." | ||
or | ||
exists(string part | node = avoidHere(part) | | ||
part = "here" and | ||
msg = | ||
"Try to use a descriptive phrase instead of \"here\". Use \"this location\" if you can't get around mentioning the current location." | ||
or | ||
part = "this location" and | ||
msg = "Try to more descriptive phrase instead of \"this location\" if possible." | ||
) | ||
or | ||
node = avoidArticleInLinkText(_) and | ||
msg = "Avoid starting a link text with an indefinite article." | ||
or | ||
node = dontQuoteSubstitutions(_) and | ||
msg = "Don't quote substitutions in alert messages." | ||
kaeluka marked this conversation as resolved.
Show resolved
Hide resolved
|
||
or | ||
node = wrongFlowsPhrase(_, "data") and | ||
msg = "Use \"flows to\" instead of \"depends on\" in data flow queries." | ||
or | ||
node = wrongFlowsPhrase(_, "taint") and | ||
msg = "Use \"depends on\" instead of \"flows to\" in taint tracking queries." | ||
) | ||
select node, msg |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.