Skip to content

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 8 commits into from
Sep 20, 2022
Merged
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
5 changes: 5 additions & 0 deletions docs/query-metadata-style-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -179,8 +179,13 @@ The select clause of each alert query defines the alert message that is displaye
* The message should factually describe the problem that is being highlighted–it should not contain recommendations about how to fix the problem or value judgements.
* Program element references should be in 'single quotes' to distinguish them from ordinary words. Quotes are not needed around substitutions (`$@`).
* Avoid constant alert message strings and include some context, if possible. For example, `The class 'Foo' is duplicated as 'Bar'.` is preferable to `This class is duplicated here.`
* If a reference to the current location can't be avoided use "this location" instead of "here". For example, `Bad thing at this location.` is preferable to `Bad thing here.`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not obvious to me why 'this location' would be better. I don't mind the rule, because consistent writing is a good thing — but can you add a half sentence to explain the reasoning?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I remember someone suggesting it to avoid the "click here" anti-pattern, but now I can't find that review comment.

I've inserted a short sentence referencing the "click here" anti-pattern in the end.

Copy link
Contributor Author

@erik-krogh erik-krogh Sep 14, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering a bit if we should keep this line.

@aibaars suggested in a Slack comment that the "this location" phrasing got around the general "click-here" anti-pattern.
But as @smowton points out, it might not always make the text clearer: #10413 (comment)

Maybe the guidance should be that a link-text shouldn't be "here"?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In most cases could you just linkify the immediately preceding text, so something like

This sensitive data is written to a logfile unescaped /here/ -> This sensitive data is /written to a logfile unescaped/?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then you can keep here when it refers to "[some statement, expression, argument or result on] the line of this alert" and replace it when it's referring to a second thing involved in the alert.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've tried to revise the text a bit, and moved the the parts about link-texts into a separate sub-section.

In most cases could you just linkify the immediately preceding text.

Yes, and the guide now includes that 👍


I still have the guidance about "this location" when you have an alert message without links, for when you can't get around mentioning the current location in the message.

* Where you reference another program element, link to it if possible using a substitution (`$@`). Links should be used inline in the sentence, rather than as parenthesised lists or appositions.
* When a message contains multiple links, construct a sentence that has the most variable link (that is, the link with most targets) last. For further information, see [Defining the results of a query](https://codeql.github.com/docs/writing-codeql-queries/defining-the-results-of-a-query/).
* Make link texts as concise and precise as possible. E.g. avoid starting a link text with an indefinite article (a, an). For example `Path construction depends on a [user-provided value]` is preferable to `Path construction depends on [a user-provided value]`. (Where the square brackets indicate a link.) See [the W3C guide on link texts](https://www.w3.org/WAI/WCAG22/Understanding/link-purpose-in-context.html) for further information.
* For path queries, if possible, try to follow the template: `This path depends on a [user-provided value].`, or alternatively (if the first option doesn't work) `[User-provided value] flows to this location and is used in a path.`.
* Taint tracking queries generally have that a sink "depends on" the source, and dataflow queries generally have a source that "flows to" the sink.


For examples of select clauses and alert messages, see the query source files at the following pages:

Expand Down
140 changes: 140 additions & 0 deletions ql/ql/src/queries/style/AlertMessage.ql
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
/**
* @name Alert messages style violation
* @description Alert message that doesn't follow some aspect of the style guide.
* See the style guide here: https://github.com/github/codeql/blob/main/docs/query-metadata-style-guide.md#alert-messages
* @kind problem
* @problem.severity warning
* @id ql/alert-messages-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()
)
}

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("%?")
}

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].*")
}

// see https://www.w3.org/WAI/WCAG22/Understanding/link-purpose-in-context.html
String avoidHere(string part) {
part = ["here", "this location"] and // TODO: prefer "this location" of the two.
(
result.getValue().regexpMatch(".*\\b" + part + "\\b.*") and
result = getSelectPart(_, _)
)
}

// see https://www.w3.org/WAI/WCAG22/Understanding/link-purpose-in-context.html
String avoidArticleInLinkText(Select sel) {
result = sel.getExpr((any(int i | i > 1))) and
result = getSelectPart(sel, _) and
result.getValue().regexpMatch("a|an .*")
}

String dontQuoteSubstitutions(Select sel) {
result = getSelectPart(sel, _) and
result.getValue().matches(["%'$@'%", "%\"$@\"%"])
}

// "data" or "taint"
string getQueryKind(Select s) {
exists(TypeExpr sup |
sup = s.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"
)
}

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."
or
node = shouldStartCapital(_) and
msg = "Alert message should start with a capital letter."
or
exists(string part | node = avoidHere(part) |
part = "here" and
msg = "Avoid using the phrase \"" + part + "\" in alert messages."
or
part != "here" and
msg = "Try to avoid using the phrase \"" + part + "\" in alert messages 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."
or
node = wrongFlowsPhrase(_, "data") and
msg = "Use \"flows to\" instead of \"depends on\" in taint tracking queries."
or
node = wrongFlowsPhrase(_, "taint") and
msg = "Use \"depends on\" instead of \"flows to\" in data flow queries."
)
select node, msg