::nsmcp::Object ::nsmcp::Object dict getdef ::nx::Object ::nx::Object ::nsmcp::Object->::nx::Object ::nsmcp::LogRedactor ::nsmcp::LogRedactor identifiers message page queryValues record sensitive ::nsmcp::LogRedactor->::nsmcp::Object

Class ::nsmcp::LogRedactor

::nsmcp::LogRedactor[i] create ... \
           [ -headerNames (default "authorization proxy-authorization cookie set-cookie x-api-key") ] \
           [ -maskPeerAddresses:boolean (default "true") ] \
           [ -maskUserIdentifiers:boolean (default "true") ] \
           [ -queryParameters (default "__csrf_token csrf_token token access_token refresh_token id_token password passwd secret api_key apikey") ] \
           [ -replacement (default "[REDACTED]") ]

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

Class Relations

  • class: ::nx::Class[i]
  • superclass: ::nsmcp::Object[i]
::nx::Class create ::nsmcp::LogRedactor \
     -superclass ::nsmcp::Object

Methods (to be applied on instances)

  • message (scripted, public)

     <instance of nsmcp::LogRedactor[i]> message text

    Redact sensitive values in a log message. Apply configured header and token policies line by line before returning diagnostic evidence.

    Parameters:
    text (required)
    Text to inspect or redact.
    Returns:
    The redacted message text.

    Testcases:
    No testcase defined.
    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]
  • page (scripted, public)

     <instance of nsmcp::LogRedactor[i]> page page

    Redact every record in a result page. Retain pagination and coverage metadata while replacing its records with redacted copies.

    Parameters:
    page (required)
    Result-page dictionary containing records and coverage metadata.
    Returns:
    The redacted page dictionary.

    Testcases:
    No testcase defined.
    set records {}
    foreach entry [dict get $page records] {
        lappend records [:record $entry]
    }
    dict set page records $records
    return $page
  • queryValues (scripted, public)

     <instance of nsmcp::LogRedactor[i]> queryValues text

    Redact sensitive query-parameter values in text. Decode names for policy matching while retaining the original URL spelling and avoiding value decoding.

    Parameters:
    text (required)
    Text to inspect or redact.
    Returns:
    Text with matching query values redacted.

    Testcases:
    No testcase defined.
    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
  • record (scripted, public)

     <instance of nsmcp::LogRedactor[i]> record entry

    Redact one structured log record. Redact supported message and metadata fields and retain the record's redaction indicators.

    Parameters:
    entry (required)
    Structured log-record dictionary.
    Returns:
    The redacted record dictionary.

    Testcases:
    No testcase defined.
    set original $entry
    if {[dict exists $entry message]} {
        dict set entry message [:message [dict get $entry message]]
    }
    if {[dict exists $entry mutexName]} {
        dict set entry mutexName [:message [dict get $entry mutexName]]
    }
    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