Code-Analyse: Quoted Blocks identifizieren

    • VB.NET
    • .NET (FX) 4.0

    Es gibt 1 Antwort in diesem Thema. Der letzte Beitrag () ist von Kasi.

      Code-Analyse: Quoted Blocks identifizieren

      Manchmal will man Code analysieren - zB Sql auf unerwünschte Schlüsselworte - siehe: Gegen SqlInjection
      Da stösst man auf das Problem der Quotes, nämlich gequotetes ist ja (in jeder Sprache) syntaktisch ein irrelevanter Block.
      Unsere Code-Analyse darf also keinesfalls QuotedBlocks mit-analysieren - es käme zu falschen Ergebnissen.

      Zunächstmal sieht das einfach aus - Beispiel:

      SQL-Abfrage

      1. Where a = "Update"
      Man kann ja einfach am " splitten, und verarbeitet nur jedes zweite StringSegment.

      Allerdings kennt Sql mehrere Quote-Marks, zB gibts auch ' - was machen wir also mit dem hier?

      SQL-Abfrage

      1. Where a = "Update" OR a = 'Insert'
      Da wirds ungemütlich - wir müssen erst nach " splitten, und das gesplittete, was noch verarbeitet wird nach ' splitten.
      Oder wir splitten nach beidem: "', müssen aber aufpassen, dass beim jeweiligen Segment der Start-Quote mit dem End-Quote übereinstimmt.
      Etwas wie dieses ist ja als quote-syntaktisch falsch abzulehnen:

      SQL-Abfrage

      1. Where a = "Update' OR a = "Insert'
      Andererseits dieses hier:

      SQL-Abfrage

      1. Where a = "Upd ' ate" OR a = 'Ins " ert'
      ist syntaktisch korrekt: Ein Quote-Block kann Quote-Marks anderer Art(en) einschliessen (und somit syntaktisch deaktivieren).
      Das ist interessant, denn ein Quote wegnehmen erzeugt u.U. einen anderen, neuen Quote-Block:

      SQL-Abfrage

      1. Where a = Upd ' ate" OR a = 'Ins " ert'
      Da fragt sich doch der Split-Algo: Warum zum Kuckuck lautet der Quote-Block nun genau: ' ate" OR a = ', und nicht: " OR a = 'Ins " oder: 'Ins " ert'?
      Antwort: Es gilt Leserichtung von links nach rechts, und der erste auftretende Quote gewinnt.
      Pfui! - Leserichtung! Split() ist draussen - kannenet :cursing:

      Aber Regex kann - Regex liest standardmässig von links nach rechts.
      Also hab ich nun diesen Regex-Trick erfunden, um den's hier geht:

      VB.NET-Quellcode

      1. Private _rgxQuotes As New Regex("'.*?'|"".*?""", RegexOptions.Singleline)
      2. Private _marker As Char = Convert.ToChar(&HF018) ' ein Zeichen, dass nie vorkommt
      3. Public Function SplitQuotes(s As String) As String()
      4. s = _rgxQuotes.Replace(s, $"{_marker}$0{_marker}")
      5. Return s.Split(_marker)
      6. End Function
      Der Regex-Pattern erkennt sowohl Single- als auch Double-Quote-Blocks. Im Replace ersetzt er sie durch sich selbst - also ersetzt er sie gar nicht.
      Sondern er fügt davor und danach ein Marker-Zeichen ein.
      Anschliessend wird an den Markern gesplittet, und wir erhalten das Sql wunderbar zerteilt als Array, bei dem jedes zweite Element gequoted ist:

      Quellcode

      1. Input: Where a = "Upd ' ate" OR a = 'Ins " ert'
      2. 0 unquoted: Where a =
      3. 1 quoted: "Upd ' ate"
      4. 2 unquoted: OR a =
      5. 3 quoted: 'Ins " ert'
      6. 4 unquoted:
      Beachte Ausgabe 4: Wenn der Input auf einem Quote-Mark endet, folgt trotzdem noch ein unquoted Segment - ist dann halt leer.

      So, wir können nun also sicher Quote-Blocks identifizieren - ja, es ist nun sogar möglich, Quote-Syntaxfehler auszumachen, also nicht-geschlossene Quote-Blocks!
      Nämlich wenn ein öffnendes Quote-Mark in einem unquoted-Segment auftritt:

      VB.NET-Quellcode

      1. Private _rgxQuotes As New Regex("'.*?'|"".*?""", RegexOptions.Singleline)
      2. Private _OpenQuoteMarks As List(Of String) = GetOpenQuoteMarks()
      3. Private _marker As Char = Convert.ToChar(&HF018) ' ein Zeichen, dass nie vorkommt
      4. Private Function GetOpenQuoteMarks() As List(Of String)
      5. Dim pattern = Regex.Unescape(_rgxQuotes.ToString)
      6. Return (From p In pattern.Split("|"c) Select p.Split("."c)(0)).ToList
      7. End Function
      8. Public Function SplitQuotes(s As String) As String()
      9. Dim splts = _rgxQuotes.Replace(s, $"{_marker}$0{_marker}").Split(_marker)
      10. For i = 0 To splts.Length - 1 Step 2
      11. Dim splt = splts(i)
      12. If _OpenQuoteMarks.Any(Function(x) splt.Contains(x)) Then
      13. Throw New InvalidOperationException("unclosed quote-mark detected")
      14. End If
      15. Next
      16. Return splts
      17. End Function

      "Öffnendes Quote-Mark"?? Quote-Marks sind doch jeweils gleich, also vorne wie hinten ", oder '!
      Tja, aber zB Sql kennt auch diese QuoteMarks: [] (und der Vollständigkeit halber diesen auch noch: `)
      Und auch Kommentare zählen zu den Quotes - weil gelten dieselben Regeln:
      Leserichtung links->rechts, und ein Comment kann ein QuoteMark auskommentieren, ein Quote kann aber auch ein Kommentar-Zeichen quoten:

      SQL-Abfrage

      1. Where a = "Upd ' ate" OR a = 'Ins " ert'
      2. OR [banane] = "Upd -- ate" OR a = 'Ins " ert'
      3. OR [banane] = "Update" -- OR a = 'Ins " ert'
      4. OR [banane] /* = "Update OR a */ = 'Ins " ert'
      5. OR [banane] = "Upd /* ate" OR a = 'Ins */ ert'
      6. OR [b'nane] = "Upd /* ate" OR a = 'Ins */ ert'
      Tja - doof: Der Vbp-Syntax-Highlighter kriegts in der letzten Zeile leider nicht hin, []-gequotetes korrekt darzustellen :thumbdown:
      Hier zum Beweis der Syntax noch Bildle vom SqlServer:


      Jedenfalls für Sql würde der Quote-Regex-pattern aller Quotes so lauten:
      '.*?'|`.*?`|\[.*?]|"".*?""|/\*.*?\*/|--.*?\n

      Noch Bemerkung zur angehängten Sample-App: Es ist eine Konsole-Anwendung, die Ausgabe erfolgt aber via Debug.Print.
      Daher rauscht das Ding einfach durch, und das Ergebnis kann man im Output-Fenster des VisualStudios angucken (als Debug kompilieren).
      Dateien

      Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „ErfinderDesRades“ ()