Skip to content

fix: REVOKE-vs-GRANT bug + escape ' in CREATE LOGIN passwords#38

Merged
robert-chiniquy merged 3 commits into
mainfrom
rch/bugfix/revoke-permission-issues-grant
May 20, 2026
Merged

fix: REVOKE-vs-GRANT bug + escape ' in CREATE LOGIN passwords#38
robert-chiniquy merged 3 commits into
mainfrom
rch/bugfix/revoke-permission-issues-grant

Conversation

@robert-chiniquy
Copy link
Copy Markdown
Contributor

@robert-chiniquy robert-chiniquy commented May 20, 2026

Two sprintf-related bugs in pkg/mssqldb/, both surfaced via the same static-analysis detector.

1. RevokePermissionOnDatabase issues REVOKE, not GRANT

RevokePermissionOnDatabase was building a GRANT ... TO statement instead of REVOKE ... FROM, so calling the revoke path re-granted the permission rather than removing it. The neighbouring GrantPermissionOnDatabase already builds the correct GRANT statement, so this was an inconsistency, not a missing helper.

While here, the trailing positional args (fullPermission, db, user) on the ExecContext call have been dropped -- the query string is built with fmt.Sprintf and contains no ? placeholders, so the args were inert.

A revoke operation against this connector would observe the permission staying granted (or being re-asserted), looking like a silent revoke that didn't take effect.

2. CreateLogin: escape ' in passwords

CreateLogin builds CREATE LOGIN ... WITH PASSWORD = '%s' via fmt.Sprintf, interpolating the caller-supplied password into a single-quoted SQL Server string literal without escaping. A password containing ' breaks the literal -- syntactically invalid for benign input, a SQL injection vector if any caller passes user-controlled text through.

SQL Server escapes ' inside string literals by doubling it (''), so this replaces single quotes with doubled single quotes before interpolation. The stored password byte-string still contains ' as authored.

how I found these

Static analysis with occult-go-analysis, c1 profile, sql_query_via_sprintf detector flagged a number of functions in this package. The other flagged functions validate inputs against []"'; characters before interpolation and are fine; the two above did not, leaving them exposed. Reading the Revoke function carefully also surfaced the GRANT-vs-REVOKE swap.

RevokePermissionOnDatabase was building a GRANT...TO statement instead
of REVOKE...FROM, so calling the revoke path re-granted the permission
instead of removing it. The neighbouring GrantPermissionOnDatabase
already builds the correct GRANT statement, so this was an
inconsistency, not a missing helper.

While here, drop the trailing positional args (fullPermission, db,
user) from the ExecContext call -- the query string is built with
fmt.Sprintf and contains no ? placeholders, so the args were inert.

How found:
  occult-go-analysis c1 profile, sql_query_via_sprintf detector
  flagged the function for review. The detector finding itself is a
  false positive (input is validated against [\]"';]), but reading
  the function in detail surfaced the GRANT-vs-REVOKE swap.

Origin:
  https://github.com/conductorone/occult-go-analysis
@robert-chiniquy robert-chiniquy requested a review from arreyder May 20, 2026 19:20
CreateLogin builds CREATE LOGIN ... WITH PASSWORD = '%s' via
fmt.Sprintf, interpolating the caller-supplied password into a
single-quoted SQL Server string literal without escaping. A password
containing ' breaks the literal -- syntactically invalid for benign
input, a SQL injection vector if any caller passes user-controlled
text through.

SQL Server escapes ' inside string literals by doubling it (''), so
this replaces single quotes with doubled single quotes before
interpolation. The stored password byte-string still contains ' as
authored.

How found:
  occult-go-analysis c1 profile, sql_query_via_sprintf detector
  flagged CreateLogin. Other sprintf sites in this package validate
  inputs against ["';] before interpolation, but CreateLogin did
  not, leaving the password path exposed.

Origin:
  https://github.com/conductorone/occult-go-analysis
Comment thread pkg/mssqldb/databases.go

_, err := c.db.ExecContext(ctx, command, fullPermission, db, user)
_, err := c.db.ExecContext(ctx, command)
if err != nil {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Suggestion: The sibling GrantPermissionOnDatabase (line ~126) still passes inert positional args permission, db, user to ExecContext — the same pattern cleaned up here. Consider removing them there too for consistency.

Suggested change
if err != nil {
_, err := c.db.ExecContext(ctx, command)

@robert-chiniquy robert-chiniquy changed the title fix: RevokePermissionOnDatabase issues REVOKE, not GRANT fix: REVOKE-vs-GRANT bug + escape ' in CREATE LOGIN passwords May 20, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 20, 2026

Connector PR Review: fix: REVOKE-vs-GRANT bug + escape ' in CREATE LOGIN passwords

Blocking Issues: 0 | Suggestions: 0 | Threads Resolved: 0
Review mode: incremental since c64c590
View review run

Review Summary

The new commits address both prior suggestions. GrantPermissionOnDatabase now calls ExecContext without the unused positional args, matching the Revoke fix. CreateLogin now validates username against []"'; before bracket-quoting, consistent with the rest of the package. The REVOKE-vs-GRANT fix, password escaping, and all other changes from earlier commits remain correct. No new issues found.

Security Issues

None found.

Correctness Issues

None found.

Suggestions

None.

Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

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

No blocking issues found.

Comment thread pkg/mssqldb/users.go
Comment on lines 400 to +407
loginName := fmt.Sprintf("[%s]", username)
l.Debug("creating SQL login", zap.String("login", loginName))
query = fmt.Sprintf("CREATE LOGIN %s WITH PASSWORD = '%s';", loginName, password)
// SQL Server string literals escape ' by doubling it. Without this,
// a password containing ' breaks out of the literal -- a SQL
// injection vector and at minimum a syntax error for legitimate
// users whose passwords contain '.
escapedPassword := strings.ReplaceAll(password, "'", "''")
query = fmt.Sprintf("CREATE LOGIN %s WITH PASSWORD = '%s';", loginName, escapedPassword)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Suggestion: The password escaping is correct, but username is bracket-quoted via [%s] without escaping ] — a username containing ] can break out of the identifier quoting. Other functions in this file (lines 287, 334) validate inputs against []"'; before interpolation. Consider adding the same ContainsAny guard on username here, or escaping ]]] inside the brackets (SQL Server's bracket-identifier escape).

Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

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

No blocking issues found.

Two additional fixes flagged by the bot review:

- pkg/mssqldb/databases.go: drop the inert positional args from
  GrantPermissionOnDatabase's ExecContext call. The command string
  is built via fmt.Sprintf with no ? placeholders, so the args
  were never used. Mirrors the cleanup the Revoke fix already
  applied to its sibling.

- pkg/mssqldb/users.go: reject [\]"'; in the username passed to
  CreateLogin. The function interpolates username into bracket-
  quoted SQL Server identifiers; a username containing ] would
  escape the brackets the same way an unescaped ' in the password
  would. Matches the validation pattern used by the other
  identifier-interpolating functions in this package.
Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

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

No blocking issues found.

@robert-chiniquy robert-chiniquy marked this pull request as ready for review May 20, 2026 20:18
@robert-chiniquy robert-chiniquy requested a review from a team May 20, 2026 20:18
@robert-chiniquy robert-chiniquy merged commit 6917728 into main May 20, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants