Class ::nsmcp::LogRedactor (public)

 ::nx::Class ::nsmcp::LogRedactor[i]

Defined in /usr/local/ns/tcl/nsmcp/lib/log-redaction.tcl

Testcases:
No testcase defined.
Source code:
    :property {queryParameters {__csrf_token csrf_token token access_token refresh_token id_token password passwd secret api_key apikey}}
    :property {headerNames {authorization proxy-authorization cookie set-cookie x-api-key}}
    :property {maskPeerAddresses:boolean true}
    :property {maskUserIdentifiers:boolean true}
    :property {replacement {[REDACTED]}}

    :method sensitive {name names} {
        foreach candidate $names {
            if {[string equal -nocase $name $candidate]} {return true}
        }
        return false
    }
    :public method queryValues {text} {
        set start 0
        # Match literal query separators. Decode names, never values, for policy
        # comparison; retain the original URL spelling and nonsensitive values.
        while {[regexp -indices -start $start {[?&]([^=&#\s"<>]+)=([^&#\s"<>]*)} $text match key value]} {
            lassign $key a b
            set name [string range $text $a $b]
            if {[catch {ns_urldecode -charset utf-8 -- $name} decoded]} {
                set decoded $name
            }
            lassign $value a b
            if {[:sensitive $decoded ${:queryParameters}]} {
                set text "[string range $text 0 [expr {$a-1}]]${:replacement}[string range $text [expr {$b+1}] end]"
                set start [expr {$a+[string length ${:replacement}]}]
            } else {set start [expr {[lindex $match 1]+1}]}
        }
        return $text
    }
    :method identifiers {text pattern} {
        set start 0
        while {[regexp -indices -nocase -start $start $pattern $text match prefix value]} {
            lassign $value a b
            set text [string replace $text $a $b ${:replacement}]
            set start [expr {$a+[string length ${:replacement}]}]
        }
        return $text
    }
    :public method message {text} {
        set lines {}
        foreach line [split $text \n] {
            if {[regexp {^([ \t]*(?::[ \t]*)?)([^:\s]+)[ \t]*:[ \t]*(.*)$} $line -> prefix name value] &&
                [:sensitive $name ${:headerNames}]} {
                set line "${prefix}${name}: ${:replacement}"
            } else {set line [:queryValues $line]}
            if {${:maskPeerAddresses}} {
                if {[regexp -indices {^(\S+)[ \t]+\S+[ \t]+\S+[ \t]+\[[0-9]{2}/[A-Za-z]{3}/[0-9]{4}:} $line match peer]} {
                    lassign $peer a b
                    set line [string replace $line $a $b ${:replacement}]
                }
                set line [:identifiers $line {(peer(?:address|addr)?[ \t]*[:=]?[ \t]+)([0-9A-Fa-f:.]+)(?:[ \t,;]|$)}]
            }
            if {${:maskUserIdentifiers}} {
                set line [:identifiers $line {(user_id[ \t]*[:=]?[ \t]+)([0-9]+)(?:[ \t,;]|$)}]
                # The third field in the recognized access header is the user.
                if {[regexp -indices {^\S+[ \t]+\S+[ \t]+(\S+)[ \t]+\[[0-9]{2}/[A-Za-z]{3}/[0-9]{4}:} $line match user]} {
                    lassign $user a b
                    if {[string range $line $a $b] ne "-"} {
                        set line [string replace $line $a $b ${:replacement}]
                    }
                }
            }
            lappend lines $line
        }
        return [join $lines \n]
    }
    :public method record {entry} {
        set original $entry
        if {[dict exists $entry message]} {
            dict set entry message [:message [dict get $entry message]]
        }
        if {[dict exists $entry url]} {
            dict set entry url [:queryValues [dict get $entry url]]
        }
        if {${:maskPeerAddresses} && [dict exists $entry peerAddress] &&
            [dict get $entry peerAddress] ne ""} {
            set peer [dict get $entry peerAddress]
            if {[dict exists $entry message]} {
                dict set entry message [string map [list $peer ${:replacement}] [dict get $entry message]]
            }
            dict set entry peerAddress ${:replacement}
        }
        dict set entry redacted [expr {$entry ne $original}]
        return $entry
    }
    :public method page {page} {
        set records {}
        foreach entry [dict get $page records] {lappend records [:record $entry]}
        dict set page records $records
        return $page
    }
XQL Not present:
Generic, PostgreSQL, Oracle
[ hide source ] | [ make this the default ]
Show another procedure: