Classify a SQL statement for sandbox mode and extract the new password. Returns (statement_type, new_password) where statement_type is one of: - 'alter_user' — ALTER USER ... IDENTIFIED BY ... - 'set_password' — SET PASSWORD [FOR ...] = ... - 'quit' — quit, exit, \\q
(text: str)
| 486 | |
| 487 | |
| 488 | def classify_sandbox_statement(text: str) -> tuple[str | None, str | None]: |
| 489 | """Classify a SQL statement for sandbox mode and extract the new password. |
| 490 | |
| 491 | Returns (statement_type, new_password) where statement_type is one of: |
| 492 | - 'alter_user' — ALTER USER ... IDENTIFIED BY ... |
| 493 | - 'set_password' — SET PASSWORD [FOR ...] = ... |
| 494 | - 'quit' — quit, exit, \\q |
| 495 | - None — not allowed in sandbox mode |
| 496 | """ |
| 497 | stripped = text.strip() |
| 498 | if not stripped: |
| 499 | return ('quit', None) |
| 500 | |
| 501 | try: |
| 502 | tokens = list(sqlglot.tokenize(stripped, dialect='mysql')) |
| 503 | except sqlglot.errors.TokenError: |
| 504 | tokens = [] |
| 505 | |
| 506 | if not tokens: |
| 507 | return ('quit', None) |
| 508 | |
| 509 | types = [t.token_type for t in tokens] |
| 510 | texts = [t.text.upper() for t in tokens] |
| 511 | tt = sqlglot.tokens.TokenType |
| 512 | |
| 513 | # quit, exit |
| 514 | if len(tokens) == 1 and types[0] == tt.VAR and texts[0] in ('QUIT', 'EXIT'): |
| 515 | return ('quit', None) |
| 516 | |
| 517 | # \q |
| 518 | if len(tokens) == 2 and types[0] in (tt.BACKSLASH, tt.SLASH) and texts[1] in ('Q', 'QUIT', 'EXIT'): |
| 519 | return ('quit', None) |
| 520 | |
| 521 | # ALTER USER ... |
| 522 | if len(tokens) >= 2 and types[0] == tt.ALTER and texts[1] == 'USER': |
| 523 | pw = _find_password_after_by(tokens) |
| 524 | return ('alter_user', pw) |
| 525 | |
| 526 | # SET PASSWORD ... |
| 527 | if len(tokens) >= 2 and types[0] == tt.SET and texts[1] == 'PASSWORD': |
| 528 | pw = _find_password_after_eq(tokens) |
| 529 | return ('set_password', pw) |
| 530 | |
| 531 | return (None, None) |
| 532 | |
| 533 | |
| 534 | def _find_password_after_by(tokens: list[sqlglot.tokens.Token]) -> str | None: |
no test coverage detected