-
Notifications
You must be signed in to change notification settings - Fork 978
Update patch policy generation and tests #45799
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
Changes from all commits
d2171b6
cad5c58
f6fe75a
f0589c2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -19,36 +19,90 @@ type PolicyData struct { | |||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| const ( | ||||||||||||||||||||||||||||||||
| // templateStart and templateEnd* wrap the caller-supplied exists query in an | ||||||||||||||||||||||||||||||||
| // inner set of parentheses so that any OR in the WHERE body binds before the | ||||||||||||||||||||||||||||||||
| // appended AND version_compare(...) clause. | ||||||||||||||||||||||||||||||||
| templateStart = "SELECT 1 WHERE NOT EXISTS ((" | ||||||||||||||||||||||||||||||||
| templateEndDarwin = ") AND version_compare(bundle_short_version, '%s') < 0);" | ||||||||||||||||||||||||||||||||
| templateEndWindows = ") AND version_compare(version, '%s') < 0);" | ||||||||||||||||||||||||||||||||
| // notExistsStart is prepended to the exists query body; version_compare is appended | ||||||||||||||||||||||||||||||||
| // to the same WHERE clause (inside NOT EXISTS), matching the pre-#45647 generator. | ||||||||||||||||||||||||||||||||
| notExistsStart = "SELECT 1 WHERE NOT EXISTS (" | ||||||||||||||||||||||||||||||||
| existsPrefix = "SELECT 1 FROM " | ||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| var ( | ||||||||||||||||||||||||||||||||
| ErrWrongPlatform = errors.New("platform should be darwin or windows") | ||||||||||||||||||||||||||||||||
| ErrNoExistsQuery = errors.New("exists query was not provided") | ||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| // GenerateQueryForManifest wraps the "exists" query to create a patch policy query | ||||||||||||||||||||||||||||||||
| // GenerateQueryForManifest wraps the "exists" query to create a patch policy query. | ||||||||||||||||||||||||||||||||
| func GenerateQueryForManifest(p PolicyData) (string, error) { | ||||||||||||||||||||||||||||||||
| if p.ExistsQuery == "" { | ||||||||||||||||||||||||||||||||
| return "", ErrNoExistsQuery | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| suffix, err := versionCompareSuffix(p.Platform, p.ExistsQuery, p.Version) | ||||||||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||||||||
| return "", err | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| before, _ := strings.CutSuffix(p.ExistsQuery, ";") | ||||||||||||||||||||||||||||||||
| // Escape any literal '%' in the exists query (e.g. SQL LIKE patterns) | ||||||||||||||||||||||||||||||||
| // so fmt.Sprintf doesn't interpret them as format verbs. | ||||||||||||||||||||||||||||||||
| before = strings.ReplaceAll(before, "%", "%%") | ||||||||||||||||||||||||||||||||
| before = strings.TrimSpace(before) | ||||||||||||||||||||||||||||||||
| if strings.Contains(before, " OR ") { | ||||||||||||||||||||||||||||||||
| before = parenthesizeWhereClause(before) | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| return notExistsStart + before + suffix, nil | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| // parenthesizeWhereClause wraps the WHERE body in parens when it contains OR so that | ||||||||||||||||||||||||||||||||
| // the trailing AND version_compare(...) binds to the full predicate, not just the | ||||||||||||||||||||||||||||||||
| // right-hand side of OR (SQL precedence: AND > OR). | ||||||||||||||||||||||||||||||||
| func parenthesizeWhereClause(existsQuery string) string { | ||||||||||||||||||||||||||||||||
| if !strings.HasPrefix(existsQuery, existsPrefix) { | ||||||||||||||||||||||||||||||||
| return existsQuery | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| rest := strings.TrimPrefix(existsQuery, existsPrefix) | ||||||||||||||||||||||||||||||||
| table, conditions, found := strings.Cut(rest, " WHERE ") | ||||||||||||||||||||||||||||||||
| if !found { | ||||||||||||||||||||||||||||||||
| return existsQuery | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| if !strings.Contains(conditions, " OR ") { | ||||||||||||||||||||||||||||||||
| return existsQuery | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| return existsPrefix + table + " WHERE (" + conditions + ")" | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| switch p.Platform { | ||||||||||||||||||||||||||||||||
| func versionCompareSuffix(platform, existsQuery, version string) (string, error) { | ||||||||||||||||||||||||||||||||
| column, err := versionCompareColumn(platform, existsQuery) | ||||||||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||||||||
| return "", err | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| return fmt.Sprintf(" AND version_compare(%s, '%s') < 0);", column, version), nil | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
Comment on lines
+71
to
+77
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Escape single quotes in Line 76 interpolates Suggested fix func versionCompareSuffix(platform, existsQuery, version string) (string, error) {
column, err := versionCompareColumn(platform, existsQuery)
if err != nil {
return "", err
}
- return fmt.Sprintf(" AND version_compare(%s, '%s') < 0);", column, version), nil
+ safeVersion := strings.ReplaceAll(version, "'", "''")
+ return fmt.Sprintf(" AND version_compare(%s, '%s') < 0);", column, safeVersion), nil
}As per coding guidelines: “Review all SQL queries for possible SQL injection.” 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| func versionCompareColumn(platform, existsQuery string) (string, error) { | ||||||||||||||||||||||||||||||||
| switch platform { | ||||||||||||||||||||||||||||||||
| case "darwin": | ||||||||||||||||||||||||||||||||
| return fmt.Sprintf(templateStart+before+templateEndDarwin, p.Version), nil | ||||||||||||||||||||||||||||||||
| return "bundle_short_version", nil | ||||||||||||||||||||||||||||||||
| case "windows": | ||||||||||||||||||||||||||||||||
| return fmt.Sprintf(templateStart+before+templateEndWindows, p.Version), nil | ||||||||||||||||||||||||||||||||
| if tableFromExistsQuery(existsQuery) == "file" { | ||||||||||||||||||||||||||||||||
| return "file_version", nil | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| return "version", nil | ||||||||||||||||||||||||||||||||
| default: | ||||||||||||||||||||||||||||||||
| return "", ErrWrongPlatform | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| func tableFromExistsQuery(existsQuery string) string { | ||||||||||||||||||||||||||||||||
| trimmed, _ := strings.CutSuffix(strings.TrimSpace(existsQuery), ";") | ||||||||||||||||||||||||||||||||
| if !strings.HasPrefix(trimmed, existsPrefix) { | ||||||||||||||||||||||||||||||||
| return "" | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| rest := strings.TrimPrefix(trimmed, existsPrefix) | ||||||||||||||||||||||||||||||||
| if table, _, found := strings.Cut(rest, " WHERE "); found { | ||||||||||||||||||||||||||||||||
| return table | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| if table, _, found := strings.Cut(rest, " "); found { | ||||||||||||||||||||||||||||||||
| return table | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| return "", ErrWrongPlatform | ||||||||||||||||||||||||||||||||
| return strings.TrimSpace(rest) | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| // GenerateFromInstaller creates a patch policy with all fields from an installer | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Validate
ExistsQueryhas aWHEREclause before appendingAND version_compare(...).Line 50 can emit invalid SQL for inputs like
SELECT 1 FROM apps;(... FROM apps AND version_compare(...)) and also permits unscoped matching. Guard this early and return an explicit error whenWHEREis missing.Suggested fix
var ( ErrWrongPlatform = errors.New("platform should be darwin or windows") ErrNoExistsQuery = errors.New("exists query was not provided") + ErrInvalidExistsQuery = errors.New("exists query must include a WHERE clause") ) @@ before, _ := strings.CutSuffix(p.ExistsQuery, ";") before = strings.TrimSpace(before) + if !strings.Contains(strings.ToUpper(before), " WHERE ") { + return "", ErrInvalidExistsQuery + } if strings.Contains(before, " OR ") { before = parenthesizeWhereClause(before) }As per coding guidelines: “ensure that appropriate filtering criteria are applied… Check for missing WHERE clauses…”
🤖 Prompt for AI Agents