mirror of
https://github.com/github/codeql.git
synced 2026-06-24 14:17:05 +02:00
Preparatory refactor for the shared-CFG dataflow migration. Deprecates the AstNode.getAFlowNode() cached predicate on the public Python QL API and rewrites all ~140 internal callers across lib/, src/, test/, and tools/ from `expr.getAFlowNode() = cfgNode` to `cfgNode.getNode() = expr`, using ControlFlowNode.getNode() which already exists in Flow.qll. The predicate itself is preserved (with a deprecation note pointing at the new pattern) so external users do not experience churn — they can migrate at their own pace and the AST/CFG hierarchies still get the intended untangling once the deprecation eventually elapses. Semantic noop verified by: - All 361 lib/ + src/ queries compile clean. - All 122 ControlFlow + PointsTo library-tests pass. - All 64 dataflow library-tests pass. - All 113 Variables/Exceptions/Expressions/Statements/Functions/Imports/ Security/CWE-798/ModificationOfParameterWithDefault query-tests pass. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
132 lines
4.8 KiB
Plaintext
132 lines
4.8 KiB
Plaintext
import python
|
|
private import LegacyPointsTo
|
|
|
|
/** A string constant that looks like it may be used in string formatting operations. */
|
|
class PossibleAdvancedFormatString extends StringLiteral {
|
|
PossibleAdvancedFormatString() { this.getText().matches("%{%}%") }
|
|
|
|
private predicate field(int start, int end) {
|
|
brace_pair(this, start, end) and
|
|
this.getText().substring(start, end) != "{{}}"
|
|
}
|
|
|
|
/** Gets the number of the formatting field at [start, end) */
|
|
int getFieldNumber(int start, int end) {
|
|
result = this.fieldId(start, end).toInt()
|
|
or
|
|
this.implicitlyNumberedField(start, end) and
|
|
result = count(int s | this.implicitlyNumberedField(s, _) and s < start)
|
|
}
|
|
|
|
/** Gets the text of the formatting field at [start, end) */
|
|
string getField(int start, int end) {
|
|
this.field(start, end) and
|
|
result = this.getText().substring(start, end)
|
|
}
|
|
|
|
private string fieldId(int start, int end) {
|
|
this.field(start, end) and
|
|
(
|
|
result = this.getText().substring(start, end).regexpCapture("\\{([^!:.\\[]+)[!:.\\[].*", 1)
|
|
or
|
|
result = this.getText().substring(start + 1, end - 1) and result.regexpMatch("[^!:.\\[]+")
|
|
)
|
|
}
|
|
|
|
/** Gets the name of the formatting field at [start, end) */
|
|
string getFieldName(int start, int end) {
|
|
result = this.fieldId(start, end) and
|
|
not exists(this.getFieldNumber(start, end))
|
|
}
|
|
|
|
private predicate implicitlyNumberedField(int start, int end) {
|
|
this.field(start, end) and
|
|
exists(string c | start + 1 = this.getText().indexOf(c) | c in ["}", ":", "!", "."])
|
|
}
|
|
|
|
/** Whether this format string has implicitly numbered fields */
|
|
predicate isImplicitlyNumbered() { this.implicitlyNumberedField(_, _) }
|
|
|
|
/** Whether this format string has explicitly numbered fields */
|
|
predicate isExplicitlyNumbered() { exists(this.fieldId(_, _).toInt()) }
|
|
}
|
|
|
|
/** Holds if the formatting string `fmt` contains a sequence of braces `{` of length `len`, beginning at index `index`. */
|
|
predicate brace_sequence(PossibleAdvancedFormatString fmt, int index, int len) {
|
|
exists(string text | text = fmt.getText() |
|
|
text.charAt(index) = "{" and not text.charAt(index - 1) = "{" and len = 1
|
|
or
|
|
text.charAt(index) = "{" and
|
|
text.charAt(index - 1) = "{" and
|
|
brace_sequence(fmt, index - 1, len - 1)
|
|
)
|
|
}
|
|
|
|
/** Holds if index `index` in the format string `fmt` contains an escaped brace `{`. */
|
|
predicate escaped_brace(PossibleAdvancedFormatString fmt, int index) {
|
|
exists(int len | brace_sequence(fmt, index, len) | len % 2 = 0)
|
|
}
|
|
|
|
/** Holds if index `index` in the format string `fmt` contains a left brace `{` that acts as an escape character. */
|
|
predicate escaping_brace(PossibleAdvancedFormatString fmt, int index) {
|
|
escaped_brace(fmt, index + 1)
|
|
}
|
|
|
|
private predicate inner_brace_pair(PossibleAdvancedFormatString fmt, int start, int end) {
|
|
not escaping_brace(fmt, start) and
|
|
not escaped_brace(fmt, start) and
|
|
fmt.getText().charAt(start) = "{" and
|
|
exists(string pair |
|
|
pair = fmt.getText().suffix(start).regexpCapture("(?s)(\\{([^{}]|\\{\\{)*+\\}).*", 1)
|
|
|
|
|
end = start + pair.length()
|
|
)
|
|
}
|
|
|
|
private predicate brace_pair(PossibleAdvancedFormatString fmt, int start, int end) {
|
|
inner_brace_pair(fmt, start, end)
|
|
or
|
|
not escaping_brace(fmt, start) and
|
|
not escaped_brace(fmt, start) and
|
|
exists(string prefix, string postfix, int innerstart, int innerend |
|
|
brace_pair(fmt, innerstart, innerend) and
|
|
prefix = fmt.getText().regexpFind("\\{([^{}]|\\{\\{)+\\{", _, start) and
|
|
innerstart = start + prefix.length() - 1 and
|
|
postfix = fmt.getText().regexpFind("\\}([^{}]|\\}\\})*\\}", _, innerend - 1) and
|
|
end = innerend + postfix.length() - 1
|
|
)
|
|
}
|
|
|
|
private predicate advanced_format_call(Call format_expr, PossibleAdvancedFormatString fmt, int args) {
|
|
exists(CallNode call, ControlFlowNode fmtCfg |
|
|
call.getNode() = format_expr and fmtCfg.getNode() = fmt
|
|
|
|
|
call.getFunction().(ControlFlowNodeWithPointsTo).pointsTo(Value::named("format")) and
|
|
call.getArg(0).(ControlFlowNodeWithPointsTo).pointsTo(_, fmtCfg) and
|
|
args = count(format_expr.getAnArg()) - 1
|
|
or
|
|
call.getFunction()
|
|
.(AttrNode)
|
|
.getObject("format")
|
|
.(ControlFlowNodeWithPointsTo)
|
|
.pointsTo(_, fmtCfg) and
|
|
args = count(format_expr.getAnArg())
|
|
)
|
|
}
|
|
|
|
/** A string constant that has the `format` method applied to it. */
|
|
class AdvancedFormatString extends PossibleAdvancedFormatString {
|
|
AdvancedFormatString() { advanced_format_call(_, this, _) }
|
|
}
|
|
|
|
/** A string formatting operation that uses the `format` method. */
|
|
class AdvancedFormattingCall extends Call {
|
|
AdvancedFormattingCall() { advanced_format_call(this, _, _) }
|
|
|
|
/** Count of the arguments actually provided */
|
|
int providedArgCount() { advanced_format_call(this, _, result) }
|
|
|
|
/** Gets a formatting string for this call. */
|
|
AdvancedFormatString getAFormat() { advanced_format_call(this, result, _) }
|
|
}
|