Upgrade versions of vendored libs

This commit is contained in:
eleksir
2022-11-04 16:29:14 +03:00
parent fba732cc24
commit a7ae372459
941 changed files with 219674 additions and 23611 deletions

View File

@@ -1,5 +1,9 @@
# redact
Utilities to redact Go strings for confidentiality
Godoc link: https://pkg.go.dev/github.com/cockroachdb/redact?tab=doc
![Build Status](https://github.com/cockroachdb/redact/actions/workflows/ci.yaml/badge.svg?branch=master)
[![Go Reference](https://pkg.go.dev/badge/github.com/cockroachdb/redact.svg)](https://pkg.go.dev/github.com/cockroachdb/redact)
Some explanations about how this is used in e.g. CockroachDB:
https://wiki.crdb.io/wiki/spaces/CRDB/pages/1824817806/Log+and+error+redactability

View File

@@ -14,22 +14,23 @@
package redact
import "fmt"
import (
"fmt"
"reflect"
"github.com/cockroachdb/redact/builder"
i "github.com/cockroachdb/redact/interfaces"
b "github.com/cockroachdb/redact/internal/buffer"
fw "github.com/cockroachdb/redact/internal/fmtforward"
m "github.com/cockroachdb/redact/internal/markers"
w "github.com/cockroachdb/redact/internal/redact"
ifmt "github.com/cockroachdb/redact/internal/rfmt"
)
// SafeFormatter is implemented by object types that want to separate
// safe and non-safe information when printed out by a Printf-like
// formatter.
type SafeFormatter interface {
// SafeFormat is like the Format method of fmt.Formatter, except
// that it operates using a SafePrinter instead of a fmt.State for
// output.
//
// The verb argument is the control character that defines
// the formatting mode in the surrounding Printf call.
// For example, if this method is called to format %03d,
// the verb is 'd'.
SafeFormat(s SafePrinter, verb rune)
}
type SafeFormatter = i.SafeFormatter
// SafeValue is a marker interface to be implemented by types that
// alias base Go types and whose natural representation via Printf is
@@ -48,17 +49,13 @@ type SafeFormatter interface {
// collect all the types that implement this interface, as well as all
// uses of this type, and produce a report. Changes to this report
// should receive maximal amount of scrutiny during code reviews.
type SafeValue interface {
SafeValue()
}
type SafeValue = i.SafeValue
// SafeMessager is an alternative to SafeFormatter used in previous
// versions of CockroachDB.
// NB: this interface is obsolete. Use SafeFormatter instead.
// TODO(knz): Remove this.
type SafeMessager interface {
SafeMessage() string
}
type SafeMessager = i.SafeMessager
// SafePrinter is a stateful helper that abstracts an output stream in
// the context of printf-like formatting, but with the ability to
@@ -67,60 +64,112 @@ type SafeMessager interface {
// This package provides one implementation of this using marker
// delimiters for unsafe data, see markers.go. We would like to aim
// for alternate implementations to generate more structured formats.
type SafePrinter interface {
// SafePrinter inherits fmt.State to access format flags, however
// calls to fmt.State's underlying Write() as unsafe.
fmt.State
// SafePrinter provides the SafeWriter interface.
SafeWriter
}
type SafePrinter = i.SafePrinter
// SafeWriter provides helper functions for use in implementations of
// SafeFormatter, to format mixes of safe and unsafe strings.
type SafeWriter interface {
// SafeString emits a safe string.
SafeString(SafeString)
type SafeWriter = i.SafeWriter
// SafeRune emits a safe rune.
SafeRune(SafeRune)
// SafeString represents a string that is not a sensitive value.
type SafeString = i.SafeString
// Print emits its arguments separated by spaces.
// For each argument it dynamically checks for the SafeFormatter or
// SafeValue interface and either use that, or mark the argument
// payload as unsafe.
Print(args ...interface{})
// SafeInt represents an integer that is not a sensitive value.
type SafeInt = i.SafeInt
// For printf, a linter checks that the format string is
// a constant literal, so the implementation can assume it's always
// safe.
Printf(format string, arg ...interface{})
// SafeUint represents an integer that is not a sensitive value.
type SafeUint = i.SafeUint
// UnsafeString writes an unsafe string.
UnsafeString(string)
// UnsafeByte writes an unsafe byte.
UnsafeByte(byte)
// UnsafeBytes writes an unsafe byte slice.
UnsafeBytes([]byte)
// UnsafeRune writes an unsafe rune.
UnsafeRune(rune)
}
// SafeString aliases string. This is not meant to be used directly;
// the type definition ensures that SafePrinter's SafeString method
// can only take constant string literals as arguments. Typically, a
// Go linter would ensure that ConstantString is never used to cast a
// value.
type SafeString string
// SafeValue makes SafeString a SafeValue.
func (SafeString) SafeValue() {}
// SafeFloat represents a floating-point value that is not a sensitive value.
type SafeFloat = i.SafeFloat
// SafeRune aliases rune. See the explanation for SafeString.
type SafeRune rune
type SafeRune = i.SafeRune
// SafeValue makes SafeRune a SafeValue.
func (SafeRune) SafeValue() {}
// RedactableString is a string that contains a mix of safe and unsafe
// bits of data, but where it is known that unsafe bits are enclosed
// by redaction markers ‹ and ›, and occurrences of the markers
// inside the original data items have been escaped.
//
// Instances of RedactableString should not be constructed directly;
// instead use the facilities from print.go (Sprint, Sprintf)
// or the methods below.
type RedactableString = m.RedactableString
// RedactableBytes is like RedactableString but is a byte slice.
//
// Instances of RedactableBytes should not be constructed directly;
// instead use the facilities from print.go (Sprint, Sprintf)
// or the methods below.
type RedactableBytes = m.RedactableBytes
// StartMarker returns the start delimiter for an unsafe string.
func StartMarker() []byte { return m.StartMarker() }
// EndMarker returns the end delimiter for an unsafe string.
func EndMarker() []byte { return m.EndMarker() }
// RedactedMarker returns the special string used by Redact.
func RedactedMarker() []byte { return m.RedactedMarker() }
// EscapeMarkers escapes the special delimiters from the provided
// byte slice.
func EscapeMarkers(s []byte) []byte { return m.EscapeMarkers(s) }
// EscapeBytes escapes markers inside the given byte slice and encloses
// the entire byte slice between redaction markers.
// EscapeBytes escapes markers inside the given byte slice and encloses
// the entire byte slice between redaction markers.
func EscapeBytes(s []byte) RedactableBytes { return ifmt.EscapeBytes(s) }
// ManualBuffer is a variable-sized buffer of bytes with
// Write methods. Writes are escaped in different ways
// depending on the output mode.
// Note: This type is only meant for "advanced" usage.
// For most common uses, consider using StringBuilder instead.
type ManualBuffer = b.Buffer
// Unsafe turns any value that would otherwise be considered safe,
// into an unsafe value.
func Unsafe(a interface{}) interface{} { return w.Unsafe(a) }
// Safe turns any value into an object that is considered as safe by
// the formatter.
//
// This is provided as an “escape hatch” for cases where the other
// interfaces and conventions fail. Increased usage of this mechanism
// should be taken as a signal that a new abstraction is missing.
// The implementation is also slow.
func Safe(a interface{}) SafeValue { return w.Safe(a) }
// RegisterRedactErrorFn registers an error redaction function for use
// during automatic redaction by this package.
// Provided e.g. by cockroachdb/errors.
func RegisterRedactErrorFn(fn func(err error, p i.SafePrinter, verb rune)) {
ifmt.RegisterRedactErrorFn(fn)
}
// MakeFormat is a helper for use by implementations of the
// SafeFormatter interface. It reproduces the format currently active
// in fmt.State and verb. This is provided because Go's standard
// fmt.State does not make the original format string available to us.
//
// If the return value justV is true, then the current state
// was found to be %v exactly; in that case the caller
// can avoid a full-blown Printf call and use just Print instead
// to take a shortcut.
func MakeFormat(s fmt.State, verb rune) (justV bool, format string) {
return fw.MakeFormat(s, verb)
}
// StringBuilder accumulates strings with optional redaction markers.
//
// It implements io.Writer but marks direct writes as redactable.
// To distinguish safe and unsafe bits, it also implements the SafeWriter
// interface.
type StringBuilder = builder.StringBuilder
// RegisterSafeType registers a data type to always be considered safe
// during the production of redactable strings.
func RegisterSafeType(t reflect.Type) {
ifmt.RegisterSafeType(t)
}

View File

@@ -12,13 +12,15 @@
// implied. See the License for the specific language governing
// permissions and limitations under the License.
package redact
package builder
import (
"bytes"
"fmt"
"io"
"unicode/utf8"
i "github.com/cockroachdb/redact/interfaces"
ib "github.com/cockroachdb/redact/internal/buffer"
ifmt "github.com/cockroachdb/redact/internal/rfmt"
)
// StringBuilder accumulates strings with optional redaction markers.
@@ -27,26 +29,14 @@ import (
// To distinguish safe and unsafe bits, it also implements the SafeWriter
// interface.
type StringBuilder struct {
// we use bytes.Buffer internally to simplify the implementation of
// the SafeWriter interface.
buf bytes.Buffer
ib.Buffer
}
// String returns the accumulated string, with redaction markers stripped out.
// To obtain the redactable string, call RedactableString().
func (b StringBuilder) String() string { return b.RedactableString().StripMarkers() }
var _ fmt.Stringer = StringBuilder{}
var _ fmt.Stringer = (*StringBuilder)(nil)
// RedactableString returns the accumulated string, including redaction markers.
func (b StringBuilder) RedactableString() RedactableString { return RedactableString(b.buf.String()) }
// RedactableBytes returns the accumulated bytes, including redaction markers.
func (b StringBuilder) RedactableBytes() RedactableBytes { return RedactableBytes(b.buf.Bytes()) }
// SafeFormat implements SafeFormatter.
func (b StringBuilder) SafeFormat(p SafePrinter, _ rune) {
func (b StringBuilder) SafeFormat(p i.SafePrinter, _ rune) {
// We only support the %v / %s natural print here.
// Go supports other formatting verbs for strings: %x/%X/%q.
//
@@ -64,20 +54,8 @@ func (b StringBuilder) SafeFormat(p SafePrinter, _ rune) {
p.Print(b.RedactableString())
}
var _ SafeFormatter = StringBuilder{}
var _ SafeFormatter = (*StringBuilder)(nil)
// Len returns the number of accumulated bytes, including redaction
// markers; b.Len() == len(b.RedactableString()).
func (b *StringBuilder) Len() int { return b.buf.Len() }
// Cap returns the capacity of the builder's underlying byte slice. It is the
// total space allocated for the string being built and includes any bytes
// already written.
func (b *StringBuilder) Cap() int { return b.buf.Cap() }
// Reset resets the Builder to be empty.
func (b *StringBuilder) Reset() { b.buf.Reset() }
var _ i.SafeFormatter = StringBuilder{}
var _ i.SafeFormatter = (*StringBuilder)(nil)
// StringBuilder implements io.Writer.
// Direct Write() calls are considered unsafe.
@@ -85,65 +63,75 @@ var _ io.Writer = (*StringBuilder)(nil)
// Write implements the io.Writer interface.
func (b *StringBuilder) Write(s []byte) (int, error) {
b.UnsafeBytes(s)
return len(s), nil
b.SetMode(ib.UnsafeEscaped)
return b.Buffer.Write(s)
}
// StringBuilder implements SafeWriter.
var _ SafeWriter = (*StringBuilder)(nil)
var _ i.SafeWriter = (*StringBuilder)(nil)
// Print is part of the SafeWriter interface.
func (b *StringBuilder) Print(args ...interface{}) {
_, _ = Fprint(&b.buf, args...)
b.SetMode(ib.PreRedactable)
_, _ = ifmt.Fprint(&b.Buffer, args...)
}
// Printf is part of the SafeWriter interface.
func (b *StringBuilder) Printf(format string, args ...interface{}) {
_, _ = Fprintf(&b.buf, format, args...)
b.SetMode(ib.PreRedactable)
_, _ = ifmt.Fprintf(&b.Buffer, format, args...)
}
// SafeString is part of the SafeWriter interface.
func (b *StringBuilder) SafeString(s SafeString) {
w := escapeWriter{w: &b.buf, enclose: false}
_, _ = w.Write([]byte(s))
func (b *StringBuilder) SafeString(s i.SafeString) {
b.SetMode(ib.SafeEscaped)
_, _ = b.Buffer.WriteString(string(s))
}
// SafeInt is part of the SafeWriter interface.
func (b *StringBuilder) SafeInt(s i.SafeInt) {
b.SetMode(ib.SafeEscaped)
_, _ = ifmt.Fprintf(&b.Buffer, "%d", s)
}
// SafeUint is part of the SafeWriter interface.
func (b *StringBuilder) SafeUint(s i.SafeUint) {
b.SetMode(ib.SafeEscaped)
_, _ = ifmt.Fprintf(&b.Buffer, "%d", s)
}
// SafeFloat is part of the SafeWriter interface.
func (b *StringBuilder) SafeFloat(s i.SafeFloat) {
b.SetMode(ib.SafeEscaped)
_, _ = ifmt.Fprintf(&b.Buffer, "%v", s)
}
// SafeRune is part of the SafeWriter interface.
func (b *StringBuilder) SafeRune(s SafeRune) {
if s == startRedactable || s == endRedactable {
s = escapeMark
}
_, _ = b.buf.WriteRune(rune(s))
func (b *StringBuilder) SafeRune(s i.SafeRune) {
b.SetMode(ib.SafeEscaped)
_ = b.Buffer.WriteRune(rune(s))
}
// UnsafeString is part of the SafeWriter interface.
func (b *StringBuilder) UnsafeString(s string) {
w := escapeWriter{w: &b.buf, enclose: true, strip: true}
_, _ = w.Write([]byte(s))
b.SetMode(ib.UnsafeEscaped)
_, _ = b.Buffer.WriteString(s)
}
// UnsafeRune is part of the SafeWriter interface.
func (b *StringBuilder) UnsafeRune(s rune) {
_, _ = b.buf.WriteRune(startRedactable)
b.SafeRune(SafeRune(s))
_, _ = b.buf.WriteRune(endRedactable)
b.SetMode(ib.UnsafeEscaped)
_ = b.Buffer.WriteRune(s)
}
// UnsafeByte is part of the SafeWriter interface.
func (b *StringBuilder) UnsafeByte(s byte) {
_, _ = b.buf.WriteRune(startRedactable)
if s >= utf8.RuneSelf ||
s == startRedactableBytes[0] || s == endRedactableBytes[0] {
// Unsafe byte. Escape it.
_, _ = b.buf.Write(escapeBytes)
} else {
_ = b.buf.WriteByte(s)
}
_, _ = b.buf.WriteRune(endRedactable)
b.SetMode(ib.UnsafeEscaped)
_ = b.Buffer.WriteByte(s)
}
// UnsafeBytes is part of the SafeWriter interface.
func (b *StringBuilder) UnsafeBytes(s []byte) {
w := escapeWriter{w: &b.buf, enclose: true, strip: true}
_, _ = w.Write(s)
b.SetMode(ib.UnsafeEscaped)
_, _ = b.Buffer.Write(s)
}

View File

@@ -0,0 +1,149 @@
// Copyright 2021 The Cockroach Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
// implied. See the License for the specific language governing
// permissions and limitations under the License.
package interfaces
import "fmt"
// SafeFormatter is implemented by object types that want to separate
// safe and non-safe information when printed out by a Printf-like
// formatter.
type SafeFormatter interface {
// SafeFormat is like the Format method of fmt.Formatter, except
// that it operates using a SafePrinter instead of a fmt.State for
// output.
//
// The verb argument is the control character that defines
// the formatting mode in the surrounding Printf call.
// For example, if this method is called to format %03d,
// the verb is 'd'.
SafeFormat(s SafePrinter, verb rune)
}
// SafePrinter is a stateful helper that abstracts an output stream in
// the context of printf-like formatting, but with the ability to
// separate safe and unsafe bits of data.
//
// This package provides one implementation of this using marker
// delimiters for unsafe data, see markers.go. We would like to aim
// for alternate implementations to generate more structured formats.
type SafePrinter interface {
// SafePrinter inherits fmt.State to access format flags, however
// calls to fmt.State's underlying Write() as unsafe.
fmt.State
// SafePrinter provides the SafeWriter interface.
SafeWriter
}
// SafeWriter provides helper functions for use in implementations of
// SafeFormatter, to format mixes of safe and unsafe strings.
type SafeWriter interface {
// SafeString emits a safe string.
SafeString(SafeString)
// SafeInt emits a safe integer.
SafeInt(SafeInt)
// SafeUint emits a safe unsigned integer.
SafeUint(SafeUint)
// SafeFloat emits a safe floating-point value.
SafeFloat(SafeFloat)
// SafeRune emits a safe rune.
SafeRune(SafeRune)
// Print emits its arguments separated by spaces.
// For each argument it dynamically checks for the SafeFormatter or
// SafeValue interface and either use that, or mark the argument
// payload as unsafe.
Print(args ...interface{})
// For printf, a linter checks that the format string is
// a constant literal, so the implementation can assume it's always
// safe.
Printf(format string, arg ...interface{})
// UnsafeString writes an unsafe string.
UnsafeString(string)
// UnsafeByte writes an unsafe byte.
UnsafeByte(byte)
// UnsafeBytes writes an unsafe byte slice.
UnsafeBytes([]byte)
// UnsafeRune writes an unsafe rune.
UnsafeRune(rune)
}
// SafeString represents a string that is not a sensitive value.
type SafeString string
// SafeValue makes SafeString a SafeValue.
func (SafeString) SafeValue() {}
// SafeInt represents an integer that is not a sensitive value.
type SafeInt int64
// SafeValue makes SafeInt a SafeValue.
func (SafeInt) SafeValue() {}
// SafeUint represents an integer that is not a sensitive value.
type SafeUint uint64
// SafeValue makes SafeUint a SafeValue.
func (SafeUint) SafeValue() {}
// SafeFloat represents a floating-point value that is not a sensitive value.
type SafeFloat float64
// SafeValue makes SafeFloat a SafeValue.
func (SafeFloat) SafeValue() {}
// SafeRune aliases rune. See the explanation for SafeString.
type SafeRune rune
// SafeValue makes SafeRune a SafeValue.
func (SafeRune) SafeValue() {}
// SafeValue is a marker interface to be implemented by types that
// alias base Go types and whose natural representation via Printf is
// always safe for reporting.
//
// This is recognized by the SafePrinter interface as an alternative
// to SafeFormatter.
//
// It is provided to decorate "leaf" Go types, such as aliases to int.
//
// Typically, a linter enforces that a type can only implement this
// interface if it aliases a base go type. More complex types should
// implement SafeFormatter instead.
//
// It is advised to build an automatic process during builds to
// collect all the types that implement this interface, as well as all
// uses of this type, and produce a report. Changes to this report
// should receive maximal amount of scrutiny during code reviews.
type SafeValue interface {
SafeValue()
}
// SafeMessager is an alternative to SafeFormatter used in previous
// versions of CockroachDB.
// NB: this interface is obsolete. Use SafeFormatter instead.
// TODO(knz): Remove this.
type SafeMessager = interface {
SafeMessage() string
}

View File

@@ -0,0 +1,333 @@
// Copyright 2021 The Cockroach Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
// implied. See the License for the specific language governing
// permissions and limitations under the License.
package buffer
import (
origFmt "fmt"
"unicode/utf8"
"unsafe"
"github.com/cockroachdb/redact/internal/escape"
m "github.com/cockroachdb/redact/internal/markers"
)
// Buffer is a variable-sized buffer of bytes with a
// Write method. Writes are escaped in different ways
// depending on the output mode.
type Buffer struct {
buf []byte
validUntil int // exclusive upper bound of data that's already validated
mode OutputMode
markerOpen bool
}
// OutputMode determines how writes are processed in the Buffer.
type OutputMode int
const (
// UnsafeEscaped says that written data is unsafe for reporting,
// so it should be enclosed in redaction markers,
// and may contain redaction markers itself, and so it should be escaped.
UnsafeEscaped OutputMode = iota
// SafeEscaped says that written data is safe for reporting
// (should not be enclosed in redaction markers) but may itself
// contain redaction markers, and so it should be escaped.
SafeEscaped
// SafeRaw says that written data is safe for reporting
// (should not be enclosed in redaction markers) and is
// guaranteed not to contain redaction markers, so it
// needs not be escaped.
SafeRaw
// PreRedactable says that written data already contains a mixed
// of safe and unsafe information with suitably placed redaction markers,
// and so should be inlined.
PreRedactable = SafeRaw
)
// RedactableBytes returns the bytes in the buffer.
func (b Buffer) RedactableBytes() m.RedactableBytes {
// NB: we're dependent on the fact this is a copy of the original
// buffer. The finalize() method should not be called
// in a conceputally read-only accessor like RedactableBytes().
b.finalize()
return m.RedactableBytes(b.buf)
}
// RedactableString returns the bytes in the buffer.
func (b Buffer) RedactableString() m.RedactableString {
// NB: we're dependent on the fact this is a copy of the original
// buffer. The finalize() method should not be called
// in a conceputally read-only accessor like RedactableString().
b.finalize()
return m.RedactableString(b.buf)
}
// String implemens fmt.Stringer.
func (b Buffer) String() string {
// NB: we're dependent on the fact this is a copy of the original
// buffer. The finalize() method should not be called
// in a conceputally read-only accessor like String().
b.finalize()
return m.RedactableString(b.buf).StripMarkers()
}
// TakeRedactableBytes returns the buffer
// contents and reinitializes the buffer. This saves
// a memory allocation compared to RedactableBytes().
func (b *Buffer) TakeRedactableBytes() m.RedactableBytes {
b.finalize()
r := b.buf
b.buf = nil
b.validUntil = 0
b.mode = UnsafeEscaped
return m.RedactableBytes(r)
}
// TakeRedactableString returns the buffer
// contents and reinitializes the buffer. This saves
// a memory allocation compared to RedactableString().
func (b *Buffer) TakeRedactableString() m.RedactableString {
if b == nil {
// Special case, useful in debugging.
return "<nil>"
}
b.finalize()
r := *(*m.RedactableString)(unsafe.Pointer(&b.buf))
b.buf = nil
b.validUntil = 0
b.mode = UnsafeEscaped
return r
}
// Len returns the number of bytes in the buffer.
func (b *Buffer) Len() int {
copy := *b
copy.finalize()
return len(copy.buf)
}
// Cap returns the capacity of the buffer's underlying byte slice,
// that is, the total space allocated for the buffer's data.
func (b *Buffer) Cap() int {
return cap(b.buf)
}
// Write appends the contents of p to the buffer, growing the buffer as
// needed. The return value n is the length of p; err is always nil. If the
// buffer becomes too large, Write will panic with ErrTooLarge.
func (b *Buffer) Write(p []byte) (n int, err error) {
b.startWrite()
m, ok := b.tryGrowByReslice(len(p))
if !ok {
m = b.grow(len(p))
}
return copy(b.buf[m:], p), nil
}
// WriteString appends the contents of s to the buffer, growing the buffer as
// needed. The return value n is the length of s; err is always nil. If the
// buffer becomes too large, WriteString will panic with ErrTooLarge.
func (b *Buffer) WriteString(s string) (n int, err error) {
b.startWrite()
m, ok := b.tryGrowByReslice(len(s))
if !ok {
m = b.grow(len(s))
}
return copy(b.buf[m:], s), nil
}
// WriteByte emits a single byte.
func (b *Buffer) WriteByte(s byte) error {
b.startWrite()
if b.mode == UnsafeEscaped &&
(s >= utf8.RuneSelf ||
s == m.StartS[0] || s == m.EndS[0]) {
// Unsafe byte. Escape it.
_, err := b.WriteString(m.EscapeMarkS)
return err
}
m, ok := b.tryGrowByReslice(1)
if !ok {
m = b.grow(1)
}
b.buf[m] = s
return nil
}
// WriteRune emits a single rune.
func (b *Buffer) WriteRune(s rune) error {
b.startWrite()
l := utf8.RuneLen(s)
m, ok := b.tryGrowByReslice(l)
if !ok {
m = b.grow(l)
}
_ = utf8.EncodeRune(b.buf[m:], s)
return nil
}
// finalize ensures that all the buffer is properly
// marked.
func (b *Buffer) finalize() {
if b.mode == SafeRaw {
b.validUntil = len(b.buf)
} else {
b.escapeToEnd(b.mode == UnsafeEscaped /* breakNewLines */)
}
if b.markerOpen {
b.endRedactable()
b.validUntil = len(b.buf)
}
}
// endRedactable adds the closing redaction marker.
func (b *Buffer) endRedactable() {
if len(b.buf) == 0 {
return
}
p, ok := b.tryGrowByReslice(len(m.EndS))
if !ok {
p = b.grow(len(m.EndS))
}
copy(b.buf[p:], m.EndS)
b.markerOpen = false
}
func (b *Buffer) startWrite() {
if b.mode == UnsafeEscaped && !b.markerOpen {
b.startRedactable()
b.validUntil = len(b.buf)
}
}
// endRedactable adds the closing redaction marker.
func (b *Buffer) startRedactable() {
p, ok := b.tryGrowByReslice(len(m.StartS))
if !ok {
p = b.grow(len(m.StartS))
}
copy(b.buf[p:], m.StartS)
b.markerOpen = true
}
// escapeToEnd escapes occurrences of redaction markers in
// b.buf[b.validUntil:] and advances b.validUntil until the end.
func (b *Buffer) escapeToEnd(breakNewLines bool) {
b.buf = escape.InternalEscapeBytes(b.buf, b.validUntil, breakNewLines, breakNewLines)
b.validUntil = len(b.buf)
}
// GetMode retrieves the output mode.
func (b *Buffer) GetMode() OutputMode {
return b.mode
}
// SetMode changes the output mode.
func (b *Buffer) SetMode(newMode OutputMode) {
if b.mode == newMode {
// noop
return
}
if b.mode == UnsafeEscaped || b.mode == SafeEscaped {
b.escapeToEnd(b.mode == UnsafeEscaped /* breakNewLines */)
}
if b.markerOpen {
b.endRedactable()
}
b.validUntil = len(b.buf)
b.mode = newMode
}
// Reset resets the buffer to be empty,
// but it retains the underlying storage for use by future writes.
// It also resets the output mode to UnsafeEscaped.
func (b *Buffer) Reset() {
b.buf = b.buf[:0]
b.validUntil = 0
b.mode = UnsafeEscaped
b.markerOpen = false
}
// tryGrowByReslice is a inlineable version of grow for the fast-case where the
// internal buffer only needs to be resliced.
// It returns the index where bytes should be written and whether it succeeded.
func (b *Buffer) tryGrowByReslice(n int) (int, bool) {
if l := len(b.buf); n <= cap(b.buf)-l {
b.buf = b.buf[:l+n]
return l, true
}
return 0, false
}
// grow grows the buffer to guarantee space for n more bytes.
// It returns the index where bytes should be written.
// If the buffer can't grow it will panic with ErrTooLarge.
func (b *Buffer) grow(n int) int {
m := len(b.buf)
// Try to grow by means of a reslice.
if i, ok := b.tryGrowByReslice(n); ok {
return i
}
if b.buf == nil && n <= smallBufferSize {
b.buf = make([]byte, n, smallBufferSize)
return 0
}
c := cap(b.buf)
const maxInt = int(^uint(0) >> 1)
if n <= c/2-m {
// let capacity get twice as large.
} else if c > maxInt-c-n {
panic(ErrTooLarge)
} else {
// Not enough space anywhere, we need to allocate.
buf := makeSlice(2*c + n)
copy(buf, b.buf)
b.buf = buf
}
b.buf = b.buf[:m+n]
return m
}
// Grow grows the buffer's capacity, if necessary, to guarantee space for
// another n bytes. After Grow(n), at least n bytes can be written to the
// buffer without another allocation.
// If n is negative, Grow will panic.
// If the buffer can't grow it will panic with ErrTooLarge.
func (b *Buffer) Grow(n int) {
if n < 0 {
panic(origFmt.Errorf("redact.Buffer.Grow: negative count"))
}
m := b.grow(n)
b.buf = b.buf[:m]
}
// makeSlice allocates a slice of size n. If the allocation fails, it panics
// with ErrTooLarge.
func makeSlice(n int) []byte {
// If the make fails, give a known error.
defer func() {
if recover() != nil {
panic(ErrTooLarge)
}
}()
return make([]byte, n)
}
// ErrTooLarge is passed to panic if memory cannot be allocated to
// store data in a buffer.
var ErrTooLarge = origFmt.Errorf("redact.Buffer: too large")
// smallBufferSize is an initial allocation minimal capacity.
const smallBufferSize = 64

View File

@@ -0,0 +1,129 @@
// Copyright 2020 The Cockroach Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
// implied. See the License for the specific language governing
// permissions and limitations under the License.
package escape
import (
"bytes"
"unicode/utf8"
m "github.com/cockroachdb/redact/internal/markers"
)
// InternalEscapeBytes escapes redaction markers in the provided buf
// starting at the location startLoc.
// The bytes before startLoc are considered safe (already escaped).
//
// If breakNewLines is set, a closing redaction marker
// is placed before sequences of one or more newline characters,
// and an open redaction marker is placed afterwards.
func InternalEscapeBytes(b []byte, startLoc int, breakNewLines, strip bool) (res []byte) {
// Note: we use len(...RedactableS) and not len(...RedactableBytes)
// because the ...S variant is a compile-time constant so this
// accelerates the loops below.
start, ls := m.StartBytes, len(m.StartS)
end, le := m.EndBytes, len(m.EndS)
escape := m.EscapeMarkBytes
// Trim final newlines/spaces, for convenience.
if strip {
end := len(b)
for i := end - 1; i >= startLoc; i-- {
if b[i] == '\n' || b[i] == ' ' {
end = i
} else {
break
}
}
b = b[:end]
}
// res is the output slice. In the common case where there is
// nothing to escape, the input slice is returned directly
// and no allocation takes place.
res = b
// copied is true if and only if `res` is a copy of `b`. It only
// turns to true if the loop below finds something to escape.
copied := false
// k is the index in b up to (and excluding) the byte which we've
// already copied into res (if copied=true).
k := 0
for i := startLoc; i < len(b); i++ {
if breakNewLines && b[i] == '\n' {
if !copied {
// We only allocate an output slice when we know we definitely
// need it.
res = make([]byte, 0, len(b))
copied = true
}
res = append(res, b[k:i]...)
res = append(res, end...)
// Advance to the last newline character. We want to forward
// them all in a single call to doWrite, for performance.
lastNewLine := i
for lastNewLine < len(b) && b[lastNewLine] == '\n' {
lastNewLine++
}
res = append(res, b[i:lastNewLine]...)
res = append(res, start...)
k = lastNewLine
i = lastNewLine - 1
} else
// Ensure that occurrences of the delimiter inside the string get
// escaped.
// Reminder: ls and le are likely greater than 1, as we are scanning
// utf-8 encoded delimiters (the utf-8 encoding is multibyte).
if i+ls <= len(b) && bytes.Equal(b[i:i+ls], start) {
if !copied {
// We only allocate an output slice when we know we definitely
// need it.
res = make([]byte, 0, len(b)+len(escape))
copied = true
}
res = append(res, b[k:i]...)
res = append(res, escape...)
// Advance the counters by the length (in bytes) of the delimiter.
k = i + ls
i += ls - 1 /* -1 because we have i++ at the end of every iteration */
} else if i+le <= len(b) && bytes.Equal(b[i:i+le], end) {
if !copied {
// See the comment above about res allocation.
res = make([]byte, 0, len(b)+len(escape))
copied = true
}
res = append(res, b[k:i]...)
res = append(res, escape...)
// Advance the counters by the length (in bytes) of the delimiter.
k = i + le
i += le - 1 /* -1 because we have i++ at the end of every iteration */
}
}
// If the string terminates with an invalid utf-8 sequence, we
// want to avoid a run-in with a subsequent redaction marker.
if r, s := utf8.DecodeLastRune(b); s == 1 && r == utf8.RuneError {
if !copied {
// See the comment above about res allocation.
res = make([]byte, 0, len(b)+len(escape))
copied = true
}
res = append(res, b[k:]...)
res = append(res, escape...)
k = len(b)
}
if copied {
res = append(res, b[k:]...)
}
return
}

View File

@@ -12,7 +12,7 @@
// implied. See the License for the specific language governing
// permissions and limitations under the License.
package redact
package fmtforward
import (
"fmt"
@@ -21,7 +21,10 @@ import (
"strings"
)
func reproducePrintf(w io.Writer, s fmt.State, verb rune, arg interface{}) {
// ReproducePrintf calls fmt.Print/fmt.Printf on the argument
// based on the details present in the fmt.State and
// writes to the io.Writer.
func ReproducePrintf(w io.Writer, s fmt.State, verb rune, arg interface{}) {
justV, revFmt := MakeFormat(s, verb)
if justV {
// Common case, avoids generating then parsing the format again.
@@ -31,8 +34,7 @@ func reproducePrintf(w io.Writer, s fmt.State, verb rune, arg interface{}) {
}
}
// MakeFormat is a helper for use by implementations of the
// SafeFormatter interface. It reproduces the format currently active
// MakeFormat reproduces the format currently active
// in fmt.State and verb. This is provided because Go's standard
// fmt.State does not make the original format string available to us.
//

View File

@@ -1,83 +0,0 @@
// Copyright 2020 The Cockroach Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
// implied. See the License for the specific language governing
// permissions and limitations under the License.
package fmt
// printArg overrides the original implementation by redirecting the
// call to a substitute function in the printer, if available.
func (p *pp) printArg(a interface{}, verb rune) {
if p.printArgSubstituteFn != nil {
p.substituteState = p.printArgSubstituteFn(p, a, verb)
} else {
p.printArgOrig(a, verb)
}
}
// InternalPrinter exposes pp to the redact package.
type InternalPrinter = pp
// InternalBuffer exposes buffer to the redact package.
type InternalBuffer = buffer
// NewInternalPrinter exposes pp allocation to the redact package.
func NewInternalPrinter() *InternalPrinter { return newPrinter() }
// SetHook connects an outer printer to the inner printer.
func SetHook(
p *InternalPrinter, fn func(p *InternalPrinter, arg interface{}, verb rune) (newState int),
) {
p.printArgSubstituteFn = fn
}
// Free exposes pp deallocation to the redact package.
func Free(p *InternalPrinter) {
p.substituteState = 0
p.printArgSubstituteFn = nil
p.free()
}
// Buf exposes the string buffer to the redact package.
func Buf(p *InternalPrinter) []byte { return []byte(p.buf) }
// GetState exposes the state to the redact package.
func GetState(p *InternalPrinter) int {
return p.substituteState
}
// SetState exposes the string buffer to the redact package.
func SetState(p *InternalPrinter, b []byte) {
p.buf = buffer(b)
p.substituteState = len(p.buf)
}
// Append adds bytes to the buffer.
func Append(p *InternalPrinter, b []byte) { p.buf.write(b) }
// PrintArg exposes the printArgOrig() method to the redact package.
func PrintArg(p *InternalPrinter, a interface{}, verb rune) { p.printArgOrig(a, verb) }
// DoPrint exposes the doPrint() method to the redact package.
func DoPrint(p *InternalPrinter, a []interface{}) { p.doPrint(a) }
// DoPrintf exposes the doPrintf() method to the redact package.
func DoPrintf(p *InternalPrinter, format string, a []interface{}) { p.doPrintf(format, a) }
// SetCollectError enables wrapped error collection.
func SetCollectError(p *InternalPrinter) { p.wrapErrs = true }
// CollectingError exposes wrapped error collection.
func CollectingError(p *InternalPrinter) bool { return p.wrapErrs }
// WrappedError retrieves the wrapped error if found.
func WrappedError(p *InternalPrinter) error { return p.wrappedErr }

View File

@@ -0,0 +1,38 @@
// Copyright 2020 The Cockroach Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
// implied. See the License for the specific language governing
// permissions and limitations under the License.
package markers
import "regexp"
// Internal constants.
const (
Start = '‹'
StartS = string(Start)
End = '›'
EndS = string(End)
EscapeMark = '?'
EscapeMarkS = string(EscapeMark)
RedactedS = StartS + "×" + EndS
)
// Internal variables.
var (
StartBytes = []byte(StartS)
EndBytes = []byte(EndS)
EscapeMarkBytes = []byte(EscapeMarkS)
RedactedBytes = []byte(RedactedS)
ReStripSensitive = regexp.MustCompile(StartS + "[^" + StartS + EndS + "]*" + EndS)
ReStripMarkers = regexp.MustCompile("[" + StartS + EndS + "]")
)

View File

@@ -12,9 +12,9 @@
// implied. See the License for the specific language governing
// permissions and limitations under the License.
package redact
package markers
import "bytes"
import i "github.com/cockroachdb/redact/interfaces"
// RedactableString is a string that contains a mix of safe and unsafe
// bits of data, but where it is known that unsafe bits are enclosed
@@ -30,13 +30,13 @@ type RedactableString string
// RedactableString. This returns an unsafe string where all safe and
// unsafe bits are mixed together.
func (s RedactableString) StripMarkers() string {
return reStripMarkers.ReplaceAllString(string(s), "")
return ReStripMarkers.ReplaceAllString(string(s), "")
}
// Redact replaces all occurrences of unsafe substrings by the
// “Redacted” marker, ‹×›. The result string is still safe.
func (s RedactableString) Redact() RedactableString {
return RedactableString(reStripSensitive.ReplaceAllString(string(s), redactedS))
return RedactableString(ReStripSensitive.ReplaceAllString(string(s), RedactedS))
}
// ToBytes converts the string to a byte slice.
@@ -45,7 +45,7 @@ func (s RedactableString) ToBytes() RedactableBytes {
}
// SafeFormat formats the redactable safely.
func (s RedactableString) SafeFormat(sp SafePrinter, _ rune) {
func (s RedactableString) SafeFormat(sp i.SafePrinter, _ rune) {
// As per annotateArgs() in markers_internal_print.go,
// we consider the redactable string not further formattable.
sp.Print(s)
@@ -62,13 +62,13 @@ type RedactableBytes []byte
// RedactableBytes. This returns an unsafe string where all safe and
// unsafe bits are mixed together.
func (s RedactableBytes) StripMarkers() []byte {
return reStripMarkers.ReplaceAll([]byte(s), nil)
return ReStripMarkers.ReplaceAll([]byte(s), nil)
}
// Redact replaces all occurrences of unsafe substrings by the
// “Redacted” marker, ‹×›.
func (s RedactableBytes) Redact() RedactableBytes {
return RedactableBytes(reStripSensitive.ReplaceAll(s, redactedBytes))
return RedactableBytes(ReStripSensitive.ReplaceAll(s, RedactedBytes))
}
// ToString converts the byte slice to a string.
@@ -77,33 +77,23 @@ func (s RedactableBytes) ToString() RedactableString {
}
// SafeFormat formats the redactable safely.
func (s RedactableBytes) SafeFormat(sp SafePrinter, _ rune) {
func (s RedactableBytes) SafeFormat(sp i.SafePrinter, _ rune) {
// As per annotateArgs() in markers_internal_print.go,
// we consider the redactable bytes not further formattable.
sp.Print(s)
}
// EscapeBytes escapes markers inside the given byte slice and encloses
// the entire byte slice between redaction markers.
func EscapeBytes(s []byte) RedactableBytes {
var buf bytes.Buffer
buf.Grow(len(s) + len(startRedactableS) + len(endRedactableS))
e := escapeWriter{w: &buf, enclose: true, strip: false}
_, _ = e.Write(s)
return RedactableBytes(buf.Bytes())
}
// StartMarker returns the start delimiter for an unsafe string.
func StartMarker() []byte { return startRedactableBytes }
func StartMarker() []byte { return []byte(StartS) }
// EndMarker returns the end delimiter for an unsafe string.
func EndMarker() []byte { return endRedactableBytes }
func EndMarker() []byte { return []byte(EndS) }
// RedactedMarker returns the special string used by Redact.
func RedactedMarker() []byte { return redactedBytes }
func RedactedMarker() []byte { return []byte(RedactedS) }
// EscapeMarkers escapes the special delimiters from the provided
// byte slice.
func EscapeMarkers(s []byte) []byte {
return reStripMarkers.ReplaceAll(s, escapeBytes)
return ReStripMarkers.ReplaceAll(s, EscapeMarkBytes)
}

View File

@@ -1,58 +0,0 @@
--- print.go.orig 2020-08-26 14:49:30.396184000 +0200
+++ print.go 2020-08-26 14:50:29.330829000 +0200
@@ -1,3 +1,6 @@
+// Code generated from print.go.orig. DO NOT EDIT
+// GENERATED FILE DO NOT EDIT
+//
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
@@ -5,12 +8,16 @@
package fmt
import (
- "internal/fmtsort"
+ // CUSTOM: needed to avoid a type mismatch on Formatter.
+ origFmt "fmt"
"io"
"os"
"reflect"
"sync"
"unicode/utf8"
+
+ // CUSTOM: our own import since we can't use internal from go stdlib.
+ "github.com/cockroachdb/redact/internal/fmtsort"
)
// Strings for use with buffer.WriteString.
@@ -51,7 +58,8 @@
// The implementation of Format may call Sprint(f) or Fprint(f) etc.
// to generate its output.
type Formatter interface {
- Format(f State, c rune)
+ // CUSTOM: refer to the original type, not the one defined here.
+ Format(f origFmt.State, c rune)
}
// Stringer is implemented by any value that has a String method,
@@ -105,6 +113,10 @@
type pp struct {
buf buffer
+ // CUSTOM: hook fn for the redact package.
+ printArgSubstituteFn func(p *pp, a interface{}, verb rune) (newState int)
+ substituteState int
+
// arg holds the current item, as an interface{}.
arg interface{}
@@ -635,7 +647,8 @@
return false
}
-func (p *pp) printArg(arg interface{}, verb rune) {
+// CUSTOM: printArg() renamed to printArgOrig().
+func (p *pp) printArgOrig(arg interface{}, verb rune) {
p.arg = arg
p.value = reflect.Value{}

View File

@@ -0,0 +1,19 @@
// Copyright 2021 The Cockroach Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
// implied. See the License for the specific language governing
// permissions and limitations under the License.
// Package redact is a helper internal package that provides the
// Unsafe and Safe value wrappers. Its name is 'redact' even though a
// better name would be 'wrappers' so as to ensure that the type name
// of generated values contains the 'redact.' prefix.
package redact

View File

@@ -14,7 +14,12 @@
package redact
import "fmt"
import (
origFmt "fmt"
i "github.com/cockroachdb/redact/interfaces"
"github.com/cockroachdb/redact/internal/fmtforward"
)
// Unsafe turns any value that would otherwise be considered safe,
// into an unsafe value.
@@ -22,12 +27,20 @@ func Unsafe(a interface{}) interface{} {
return unsafeWrap{a}
}
// UnsafeWrap is the type of wrapper produced by Unsafe.
// This is exported only for use by the rfmt package.
// Client packages should not make assumptions about
// the concrete return type of Unsafe().
type UnsafeWrap = unsafeWrap
type unsafeWrap struct {
a interface{}
}
func (w unsafeWrap) Format(s fmt.State, verb rune) {
reproducePrintf(s, s, verb, w.a)
func (w unsafeWrap) GetValue() interface{} { return w.a }
func (w unsafeWrap) Format(s origFmt.State, verb rune) {
fmtforward.ReproducePrintf(s, s, verb, w.a)
}
// Safe turns any value into an object that is considered as safe by
@@ -37,27 +50,35 @@ func (w unsafeWrap) Format(s fmt.State, verb rune) {
// interfaces and conventions fail. Increased usage of this mechanism
// should be taken as a signal that a new abstraction is missing.
// The implementation is also slow.
func Safe(a interface{}) SafeValue {
func Safe(a interface{}) i.SafeValue {
return safeWrapper{a}
}
// SafeWrapper is the type of wrapper produced by Safe.
// This is exported only for use by the rfmt package.
// Client packages should not make assumptions about
// the concrete return type of Safe().
type SafeWrapper = safeWrapper
type safeWrapper struct {
a interface{}
}
var _ SafeValue = safeWrapper{}
var _ fmt.Formatter = safeWrapper{}
var _ SafeMessager = safeWrapper{}
var _ i.SafeValue = safeWrapper{}
var _ origFmt.Formatter = safeWrapper{}
var _ i.SafeMessager = safeWrapper{}
func (w safeWrapper) GetValue() interface{} { return w.a }
// SafeValue implements the SafeValue interface.
func (w safeWrapper) SafeValue() {}
// Format implements the fmt.Formatter interface.
func (w safeWrapper) Format(s fmt.State, verb rune) {
reproducePrintf(s, s, verb, w.a)
func (w safeWrapper) Format(s origFmt.State, verb rune) {
fmtforward.ReproducePrintf(s, s, verb, w.a)
}
// SafeMessage implements SafeMessager.
func (w safeWrapper) SafeMessage() string {
return fmt.Sprintf("%v", w.a)
return origFmt.Sprintf("%v", w.a)
}

View File

@@ -24,15 +24,6 @@ Overall, the original Go code is structured as follows:
characters from the format string, as well as certain *constant*
error strings (e.g. `%(BADPREC)`).
At this point **we make the following assumption**: there is no data
from the printed *arguments* that is emitted to `pp.buf` directly by a
`doPrint()` method. This is true of the implementation as of Go 1.14.
From this point, this package contains a *custom patch* onto the
default implementation: the calls to `p.printArg` performed by the
`doPrint()` methods are redirected to a function callback that is
injected by the `redact` package.
Refreshing the sources
======================

View File

@@ -1,10 +1,11 @@
// Code generated from the Go standard library. DO NOT EDIT
// GENERATED FILE DO NOT EDIT
//
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package fmt
package rfmt
import (
"strconv"
@@ -67,25 +68,17 @@ func (f *fmt) writePadding(n int) {
if n <= 0 { // No padding bytes needed.
return
}
buf := *f.buf
oldLen := len(buf)
newLen := oldLen + n
// Make enough room for padding.
if newLen > cap(buf) {
buf = make(buffer, cap(buf)*2+n)
copy(buf, *f.buf)
}
f.buf.Grow(n)
// Decide which byte the padding should be filled with.
padByte := byte(' ')
if f.zero {
padByte = byte('0')
}
// Fill padding with padByte.
padding := buf[oldLen:newLen]
for i := range padding {
padding[i] = padByte
for i := 0; i < n; i++ {
f.buf.writeByte(padByte)
}
*f.buf = buf[:newLen]
}
// pad appends b to f.buf, padded on left (!f.minus) or right (f.minus).
@@ -403,19 +396,20 @@ func (f *fmt) fmtSbx(s string, b []byte, digits string) {
f.writePadding(f.wid - width)
}
// Write the encoding directly into the output buffer.
buf := *f.buf
if f.sharp {
// Add leading 0x or 0X.
buf = append(buf, '0', digits[16])
f.buf.WriteByte('0')
f.buf.WriteByte(digits[16])
}
var c byte
for i := 0; i < length; i++ {
if f.space && i > 0 {
// Separate elements with a space.
buf = append(buf, ' ')
f.buf.WriteByte(' ')
if f.sharp {
// Add leading 0x or 0X for each element.
buf = append(buf, '0', digits[16])
f.buf.WriteByte('0')
f.buf.WriteByte(digits[16])
}
}
if b != nil {
@@ -424,9 +418,9 @@ func (f *fmt) fmtSbx(s string, b []byte, digits string) {
c = s[i] // Take a byte from the input string.
}
// Encode each byte as two hexadecimal digits.
buf = append(buf, digits[c>>4], digits[c&0xF])
f.buf.WriteByte(digits[c>>4])
f.buf.WriteByte(digits[c&0xF])
}
*f.buf = buf
// Handle padding to the right.
if f.widPresent && f.wid > width && f.minus {
f.writePadding(f.wid - width)

View File

@@ -0,0 +1,75 @@
--- format.go.orig 2021-06-18 16:45:51.065116000 +0200
+++ format.go 2021-06-18 17:30:58.628179000 +0200
@@ -1,3 +1,6 @@
+// Code generated from the Go standard library. DO NOT EDIT
+// GENERATED FILE DO NOT EDIT
+//
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
@@ -65,25 +68,17 @@
if n <= 0 { // No padding bytes needed.
return
}
- buf := *f.buf
- oldLen := len(buf)
- newLen := oldLen + n
// Make enough room for padding.
- if newLen > cap(buf) {
- buf = make(buffer, cap(buf)*2+n)
- copy(buf, *f.buf)
- }
+ f.buf.Grow(n)
// Decide which byte the padding should be filled with.
padByte := byte(' ')
if f.zero {
padByte = byte('0')
}
// Fill padding with padByte.
- padding := buf[oldLen:newLen]
- for i := range padding {
- padding[i] = padByte
+ for i := 0; i < n; i++ {
+ f.buf.writeByte(padByte)
}
- *f.buf = buf[:newLen]
}
// pad appends b to f.buf, padded on left (!f.minus) or right (f.minus).
@@ -401,19 +396,20 @@
f.writePadding(f.wid - width)
}
// Write the encoding directly into the output buffer.
- buf := *f.buf
if f.sharp {
// Add leading 0x or 0X.
- buf = append(buf, '0', digits[16])
+ f.buf.WriteByte('0')
+ f.buf.WriteByte(digits[16])
}
var c byte
for i := 0; i < length; i++ {
if f.space && i > 0 {
// Separate elements with a space.
- buf = append(buf, ' ')
+ f.buf.WriteByte(' ')
if f.sharp {
// Add leading 0x or 0X for each element.
- buf = append(buf, '0', digits[16])
+ f.buf.WriteByte('0')
+ f.buf.WriteByte(digits[16])
}
}
if b != nil {
@@ -422,9 +418,9 @@
c = s[i] // Take a byte from the input string.
}
// Encode each byte as two hexadecimal digits.
- buf = append(buf, digits[c>>4], digits[c&0xF])
+ f.buf.WriteByte(digits[c>>4])
+ f.buf.WriteByte(digits[c&0xF])
}
- *f.buf = buf
// Handle padding to the right.
if f.widPresent && f.wid > width && f.minus {
f.writePadding(f.wid - width)

View File

@@ -0,0 +1,160 @@
// Copyright 2021 The Cockroach Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
// implied. See the License for the specific language governing
// permissions and limitations under the License.
package rfmt
import (
"reflect"
i "github.com/cockroachdb/redact/interfaces"
b "github.com/cockroachdb/redact/internal/buffer"
"github.com/cockroachdb/redact/internal/escape"
m "github.com/cockroachdb/redact/internal/markers"
rwrap "github.com/cockroachdb/redact/internal/redact"
)
type overrideMode int
const (
noOverride overrideMode = iota
// Consider unsafe values as safe.
overrideSafe
// Consider safe and pre-redactable values as unsafe.
overrideUnsafe
)
func (p *pp) startUnsafe() restorer {
prevMode := p.buf.GetMode()
if p.override != overrideSafe {
p.buf.SetMode(b.UnsafeEscaped)
}
return restorer{p, prevMode, p.override}
}
func (p *pp) startPreRedactable() restorer {
prevMode := p.buf.GetMode()
if p.override != overrideUnsafe {
p.buf.SetMode(b.PreRedactable)
}
return restorer{p, prevMode, p.override}
}
func (p *pp) startSafeOverride() restorer {
prevMode := p.buf.GetMode()
prevOverride := p.override
if p.override == noOverride {
p.buf.SetMode(b.SafeEscaped)
p.override = overrideSafe
}
return restorer{p, prevMode, prevOverride}
}
func (p *pp) startUnsafeOverride() restorer {
prevMode := p.buf.GetMode()
prevOverride := p.override
if p.override == noOverride {
p.buf.SetMode(b.UnsafeEscaped)
p.override = overrideUnsafe
}
return restorer{p, prevMode, prevOverride}
}
type restorer struct {
p *pp
prevMode b.OutputMode
prevOverride overrideMode
}
func (r restorer) restore() {
r.p.buf.SetMode(r.prevMode)
r.p.override = r.prevOverride
}
func (p *pp) handleSpecialValues(
value reflect.Value, t reflect.Type, verb rune, depth int,
) (handled bool) {
switch t {
case safeWrapperType:
handled = true
defer p.startSafeOverride().restore()
p.printValue(value.Field(0), verb, depth+1)
case unsafeWrapperType:
handled = true
defer p.startUnsafeOverride().restore()
p.printValue(value.Field(0), verb, depth+1)
case redactableStringType:
handled = true
defer p.startPreRedactable().restore()
p.buf.WriteString(value.String())
case redactableBytesType:
handled = true
defer p.startPreRedactable().restore()
p.buf.Write(value.Bytes())
}
return handled
}
// Sprintfn produces a RedactableString using the provided
// SafeFormat-alike function.
func Sprintfn(printer func(w i.SafePrinter)) m.RedactableString {
p := newPrinter()
printer(p)
s := p.buf.TakeRedactableString()
p.free()
return s
}
// HelperForErrorf is a helper to implement a redaction-aware
// fmt.Errorf-compatible function in a different package. It formats
// the string according to the given format and arguments in the same
// way as Sprintf, but in addition to this if the format contains %w
// and an error object in the proper argument position it also returns
// that error object.
//
// Note: This function only works if an error redaction function
// has been injected with RegisterRedactErrorFn().
func HelperForErrorf(format string, args ...interface{}) (m.RedactableString, error) {
p := newPrinter()
p.wrapErrs = true
p.doPrintf(format, args)
e := p.wrappedErr
s := p.buf.TakeRedactableString()
p.free()
return s, e
}
// EscapeBytes escapes markers inside the given byte slice and encloses
// the entire byte slice between redaction markers.
// EscapeBytes escapes markers inside the given byte slice and encloses
// the entire byte slice between redaction markers.
func EscapeBytes(s []byte) m.RedactableBytes {
buf := make([]byte, 0, len(s)+len(m.StartS)+len(m.EndS))
buf = append(buf, m.StartS...)
start := len(buf)
buf = append(buf, s...)
buf = escape.InternalEscapeBytes(buf, start, true /* breakNewLine */, false /* strip */)
buf = append(buf, m.EndS...)
return m.RedactableBytes(buf)
}
var (
unsafeWrapperType = reflect.TypeOf(rwrap.UnsafeWrap{})
safeWrapperType = reflect.TypeOf(rwrap.SafeWrapper{})
redactableStringType = reflect.TypeOf(m.RedactableString(""))
redactableBytesType = reflect.TypeOf(m.RedactableBytes{})
)

View File

@@ -5,7 +5,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package fmt
package rfmt
import (
// CUSTOM: needed to avoid a type mismatch on Formatter.
@@ -16,8 +16,12 @@ import (
"sync"
"unicode/utf8"
// CUSTOM: our own import since we can't use internal from go stdlib.
"github.com/cockroachdb/redact/internal/fmtsort"
// CUSTOM: our own imports.
i "github.com/cockroachdb/redact/interfaces"
b "github.com/cockroachdb/redact/internal/buffer"
m "github.com/cockroachdb/redact/internal/markers"
w "github.com/cockroachdb/redact/internal/redact"
"github.com/cockroachdb/redact/internal/rfmt/fmtsort"
)
// Strings for use with buffer.WriteString.
@@ -54,12 +58,12 @@ type State interface {
Flag(c int) bool
}
// Formatter is the interface implemented by values with a custom formatter.
// The implementation of Format may call Sprint(f) or Fprint(f) etc.
// to generate its output.
// Formatter is implemented by any value that has a Format method.
// The implementation controls how State and rune are interpreted,
// and may call Sprint(f) or Fprint(f) etc. to generate its output.
type Formatter interface {
// CUSTOM: refer to the original type, not the one defined here.
Format(f origFmt.State, c rune)
Format(f origFmt.State, verb rune)
}
// Stringer is implemented by any value that has a String method,
@@ -79,43 +83,33 @@ type GoStringer interface {
GoString() string
}
// Use simple []byte instead of bytes.Buffer to avoid large dependency.
type buffer []byte
// CUSTOM: smarter buffer.
type buffer struct {
b.Buffer
}
func (b *buffer) write(p []byte) {
*b = append(*b, p...)
b.Write(p)
}
func (b *buffer) writeString(s string) {
*b = append(*b, s...)
b.WriteString(s)
}
func (b *buffer) writeByte(c byte) {
*b = append(*b, c)
b.WriteByte(c)
}
func (bp *buffer) writeRune(r rune) {
if r < utf8.RuneSelf {
*bp = append(*bp, byte(r))
return
}
b := *bp
n := len(b)
for n+utf8.UTFMax > cap(b) {
b = append(b, 0)
}
w := utf8.EncodeRune(b[n:n+utf8.UTFMax], r)
*bp = b[:n+w]
bp.WriteRune(r)
}
// pp is used to store a printer's state and is reused with sync.Pool to avoid allocations.
type pp struct {
buf buffer
// CUSTOM: hook fn for the redact package.
printArgSubstituteFn func(p *pp, a interface{}, verb rune) (newState int)
substituteState int
// CUSTOM: override safety in recursive calls.
override overrideMode
// arg holds the current item, as an interface{}.
arg interface{}
@@ -162,11 +156,11 @@ func (p *pp) free() {
// to place back in the pool.
//
// See https://golang.org/issue/23199
if cap(p.buf) > 64<<10 {
if p.buf.Cap() > 64<<10 {
return
}
p.buf = p.buf[:0]
p.buf.Reset()
p.arg = nil
p.value = reflect.Value{}
p.wrappedErr = nil
@@ -195,14 +189,16 @@ func (p *pp) Flag(b int) bool {
// Implement Write so we can call Fprintf on a pp (through State), for
// recursive use in custom verbs.
func (p *pp) Write(b []byte) (ret int, err error) {
p.buf.write(b)
return len(b), nil
func (p *pp) Write(bs []byte) (ret int, err error) {
defer p.startUnsafe().restore()
p.buf.write(bs)
return len(bs), nil
}
// Implement WriteString so that we can call io.WriteString
// on a pp (through state), for efficiency.
func (p *pp) WriteString(s string) (ret int, err error) {
defer p.startUnsafe().restore()
p.buf.writeString(s)
return len(s), nil
}
@@ -214,7 +210,7 @@ func (p *pp) WriteString(s string) (ret int, err error) {
func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) {
p := newPrinter()
p.doPrintf(format, a)
n, err = w.Write(p.buf)
n, err = w.Write([]byte(p.buf.TakeRedactableBytes()))
p.free()
return
}
@@ -226,10 +222,10 @@ func Printf(format string, a ...interface{}) (n int, err error) {
}
// Sprintf formats according to a format specifier and returns the resulting string.
func Sprintf(format string, a ...interface{}) string {
func Sprintf(format string, a ...interface{}) m.RedactableString {
p := newPrinter()
p.doPrintf(format, a)
s := string(p.buf)
s := p.buf.TakeRedactableString()
p.free()
return s
}
@@ -242,7 +238,7 @@ func Sprintf(format string, a ...interface{}) string {
func Fprint(w io.Writer, a ...interface{}) (n int, err error) {
p := newPrinter()
p.doPrint(a)
n, err = w.Write(p.buf)
n, err = w.Write([]byte(p.buf.TakeRedactableBytes()))
p.free()
return
}
@@ -256,10 +252,10 @@ func Print(a ...interface{}) (n int, err error) {
// Sprint formats using the default formats for its operands and returns the resulting string.
// Spaces are added between operands when neither is a string.
func Sprint(a ...interface{}) string {
func Sprint(a ...interface{}) m.RedactableString {
p := newPrinter()
p.doPrint(a)
s := string(p.buf)
s := p.buf.TakeRedactableString()
p.free()
return s
}
@@ -274,7 +270,7 @@ func Sprint(a ...interface{}) string {
func Fprintln(w io.Writer, a ...interface{}) (n int, err error) {
p := newPrinter()
p.doPrintln(a)
n, err = w.Write(p.buf)
n, err = w.Write([]byte(p.buf.TakeRedactableBytes()))
p.free()
return
}
@@ -288,10 +284,10 @@ func Println(a ...interface{}) (n int, err error) {
// Sprintln formats using the default formats for its operands and returns the resulting string.
// Spaces are always added between operands and a newline is appended.
func Sprintln(a ...interface{}) string {
func Sprintln(a ...interface{}) m.RedactableString {
p := newPrinter()
p.doPrintln(a)
s := string(p.buf)
s := p.buf.TakeRedactableString()
p.free()
return s
}
@@ -363,6 +359,7 @@ func (p *pp) badVerb(verb rune) {
func (p *pp) fmtBool(v bool, verb rune) {
switch verb {
case 't', 'v':
defer p.startUnsafe().restore()
p.fmt.fmtBoolean(v)
default:
p.badVerb(verb)
@@ -374,6 +371,7 @@ func (p *pp) fmtBool(v bool, verb rune) {
func (p *pp) fmt0x64(v uint64, leading0x bool) {
sharp := p.fmt.sharp
p.fmt.sharp = leading0x
defer p.startUnsafe().restore()
p.fmt.fmtInteger(v, 16, unsigned, 'v', ldigits)
p.fmt.sharp = sharp
}
@@ -385,27 +383,32 @@ func (p *pp) fmtInteger(v uint64, isSigned bool, verb rune) {
if p.fmt.sharpV && !isSigned {
p.fmt0x64(v, true)
} else {
defer p.startUnsafe().restore()
p.fmt.fmtInteger(v, 10, isSigned, verb, ldigits)
}
case 'd':
defer p.startUnsafe().restore()
p.fmt.fmtInteger(v, 10, isSigned, verb, ldigits)
case 'b':
defer p.startUnsafe().restore()
p.fmt.fmtInteger(v, 2, isSigned, verb, ldigits)
case 'o', 'O':
defer p.startUnsafe().restore()
p.fmt.fmtInteger(v, 8, isSigned, verb, ldigits)
case 'x':
defer p.startUnsafe().restore()
p.fmt.fmtInteger(v, 16, isSigned, verb, ldigits)
case 'X':
defer p.startUnsafe().restore()
p.fmt.fmtInteger(v, 16, isSigned, verb, udigits)
case 'c':
defer p.startUnsafe().restore()
p.fmt.fmtC(v)
case 'q':
if v <= utf8.MaxRune {
p.fmt.fmtQc(v)
} else {
p.badVerb(verb)
}
defer p.startUnsafe().restore()
p.fmt.fmtQc(v)
case 'U':
defer p.startUnsafe().restore()
p.fmt.fmtUnicode(v)
default:
p.badVerb(verb)
@@ -417,12 +420,16 @@ func (p *pp) fmtInteger(v uint64, isSigned bool, verb rune) {
func (p *pp) fmtFloat(v float64, size int, verb rune) {
switch verb {
case 'v':
defer p.startUnsafe().restore()
p.fmt.fmtFloat(v, size, 'g', -1)
case 'b', 'g', 'G', 'x', 'X':
defer p.startUnsafe().restore()
p.fmt.fmtFloat(v, size, verb, -1)
case 'f', 'e', 'E':
defer p.startUnsafe().restore()
p.fmt.fmtFloat(v, size, verb, 6)
case 'F':
defer p.startUnsafe().restore()
p.fmt.fmtFloat(v, size, 'f', 6)
default:
p.badVerb(verb)
@@ -453,18 +460,23 @@ func (p *pp) fmtComplex(v complex128, size int, verb rune) {
func (p *pp) fmtString(v string, verb rune) {
switch verb {
case 'v':
defer p.startUnsafe().restore()
if p.fmt.sharpV {
p.fmt.fmtQ(v)
} else {
p.fmt.fmtS(v)
}
case 's':
defer p.startUnsafe().restore()
p.fmt.fmtS(v)
case 'x':
defer p.startUnsafe().restore()
p.fmt.fmtSx(v, ldigits)
case 'X':
defer p.startUnsafe().restore()
p.fmt.fmtSx(v, udigits)
case 'q':
defer p.startUnsafe().restore()
p.fmt.fmtQ(v)
default:
p.badVerb(verb)
@@ -494,17 +506,24 @@ func (p *pp) fmtBytes(v []byte, verb rune, typeString string) {
if i > 0 {
p.buf.writeByte(' ')
}
p.fmt.fmtInteger(uint64(c), 10, unsigned, verb, ldigits)
func(p *pp, c byte, verb rune) {
defer p.startUnsafe().restore()
p.fmt.fmtInteger(uint64(c), 10, unsigned, verb, ldigits)
}(p, c, verb)
}
p.buf.writeByte(']')
}
case 's':
defer p.startUnsafe().restore()
p.fmt.fmtBs(v)
case 'x':
defer p.startUnsafe().restore()
p.fmt.fmtBx(v, ldigits)
case 'X':
defer p.startUnsafe().restore()
p.fmt.fmtBx(v, udigits)
case 'q':
defer p.startUnsafe().restore()
p.fmt.fmtQ(string(v))
default:
p.printValue(reflect.ValueOf(v), verb, 0)
@@ -535,6 +554,7 @@ func (p *pp) fmtPointer(value reflect.Value, verb rune) {
p.buf.writeByte(')')
} else {
if u == 0 {
defer p.startUnsafe().restore()
p.fmt.padString(nilAngleString)
} else {
p.fmt0x64(uint64(u), !p.fmt.sharp)
@@ -602,10 +622,37 @@ func (p *pp) handleMethods(verb rune) (handled bool) {
verb = 'v'
}
if p.override != overrideUnsafe {
switch v := p.arg.(type) {
case i.SafeFormatter:
handled = true
defer p.catchPanic(p.arg, verb, "SafeFormat")
v.SafeFormat(p, verb)
return
case i.SafeMessager:
handled = true
defer p.catchPanic(p.arg, verb, "SafeMessager")
defer p.startSafeOverride().restore()
p.fmtString(v.SafeMessage(), verb)
return
case error:
if redactErrorFn != nil {
handled = true
defer p.catchPanic(p.arg, verb, "SafeFormatter")
redactErrorFn(v, p, verb)
return
}
}
}
// Is it a Formatter?
if formatter, ok := p.arg.(Formatter); ok {
handled = true
defer p.catchPanic(p.arg, verb, "Format")
// The mode is implicit: the Format() call will
// use p.Write() which already sets the mode.
formatter.Format(p, verb)
return
}
@@ -616,6 +663,7 @@ func (p *pp) handleMethods(verb rune) (handled bool) {
handled = true
defer p.catchPanic(p.arg, verb, "GoString")
// Print the result of GoString unadorned.
defer p.startUnsafe().restore()
p.fmt.fmtS(stringer.GoString())
return
}
@@ -647,8 +695,22 @@ func (p *pp) handleMethods(verb rune) (handled bool) {
return false
}
// CUSTOM: printArg() renamed to printArgOrig().
func (p *pp) printArgOrig(arg interface{}, verb rune) {
func (p *pp) printArg(arg interface{}, verb rune) {
t := reflect.TypeOf(arg)
if safeTypeRegistry[t] {
defer p.startSafeOverride().restore()
} else if t == safeWrapperType {
defer p.startSafeOverride().restore()
arg = arg.(w.SafeWrapper).GetValue()
} else if t == unsafeWrapperType {
defer p.startUnsafeOverride().restore()
arg = arg.(w.UnsafeWrap).GetValue()
}
if _, ok := arg.(i.SafeValue); ok {
defer p.startSafeOverride().restore()
}
p.arg = arg
p.value = reflect.Value{}
@@ -714,13 +776,35 @@ func (p *pp) printArgOrig(arg interface{}, verb rune) {
case reflect.Value:
// Handle extractable values with special methods
// since printValue does not handle them at depth 0.
if f.IsValid() && f.CanInterface() {
p.arg = f.Interface()
if p.handleMethods(verb) {
if f.IsValid() {
t := f.Type()
if p.handleSpecialValues(f, t, verb, 0) {
return
}
if safeTypeRegistry[t] {
defer p.startSafeOverride().restore()
}
if f.CanInterface() {
p.arg = f.Interface()
if _, ok := p.arg.(i.SafeValue); ok {
defer p.startSafeOverride().restore()
}
if p.handleMethods(verb) {
return
}
}
}
p.printValue(f, verb, 0)
case m.RedactableString:
defer p.startPreRedactable().restore()
p.buf.WriteString(string(f))
return
case m.RedactableBytes:
defer p.startPreRedactable().restore()
p.buf.Write([]byte(f))
return
default:
// If the type is not simple, it might have methods.
if !p.handleMethods(verb) {
@@ -735,11 +819,25 @@ func (p *pp) printArgOrig(arg interface{}, verb rune) {
// It does not handle 'p' and 'T' verbs because these should have been already handled by printArg.
func (p *pp) printValue(value reflect.Value, verb rune, depth int) {
// Handle values with special methods if not already handled by printArg (depth == 0).
if depth > 0 && value.IsValid() && value.CanInterface() {
p.arg = value.Interface()
if p.handleMethods(verb) {
if depth > 0 && value.IsValid() {
t := value.Type()
if p.handleSpecialValues(value, t, verb, depth) {
return
}
if safeTypeRegistry[t] {
defer p.startSafeOverride().restore()
}
if value.CanInterface() {
p.arg = value.Interface()
if _, ok := p.arg.(i.SafeValue); ok {
defer p.startSafeOverride().restore()
}
if p.handleMethods(verb) {
return
}
}
}
p.arg = nil
p.value = value
@@ -963,7 +1061,9 @@ func parseArgNumber(format string) (index int, wid int, ok bool) {
// argNumber returns the next argument to evaluate, which is either the value of the passed-in
// argNum or the value of the bracketed integer that begins format[i:]. It also returns
// the new value of i, that is, the index of the next byte of the format to process.
func (p *pp) argNumber(argNum int, format string, i int, numArgs int) (newArgNum, newi int, found bool) {
func (p *pp) argNumber(
argNum int, format string, i int, numArgs int,
) (newArgNum, newi int, found bool) {
if len(format) <= i || format[i] != '[' {
return argNum, i, false
}
@@ -989,6 +1089,7 @@ func (p *pp) missingArg(verb rune) {
}
func (p *pp) doPrintf(format string, a []interface{}) {
p.buf.SetMode(b.SafeEscaped)
end := len(format)
argNum := 0 // we process one argument per non-trivial format
afterIndex := false // previous item in format was an index like [3].
@@ -1164,6 +1265,7 @@ formatLoop:
}
func (p *pp) doPrint(a []interface{}) {
p.buf.SetMode(b.SafeEscaped)
prevString := false
for argNum, arg := range a {
isString := arg != nil && reflect.TypeOf(arg).Kind() == reflect.String
@@ -1179,6 +1281,7 @@ func (p *pp) doPrint(a []interface{}) {
// doPrintln is like doPrint but always adds a space between arguments
// and a newline after the last argument.
func (p *pp) doPrintln(a []interface{}) {
p.buf.SetMode(b.SafeEscaped)
for argNum, arg := range a {
if argNum > 0 {
p.buf.writeByte(' ')

View File

@@ -0,0 +1,489 @@
--- print.go.orig 2021-06-18 16:45:56.623967000 +0200
+++ print.go 2021-06-19 16:26:32.751061000 +0200
@@ -1,16 +1,27 @@
+// Code generated from print.go.orig. DO NOT EDIT
+// GENERATED FILE DO NOT EDIT
+//
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package fmt
+package rfmt
import (
- "internal/fmtsort"
+ // CUSTOM: needed to avoid a type mismatch on Formatter.
+ origFmt "fmt"
"io"
"os"
"reflect"
"sync"
"unicode/utf8"
+
+ // CUSTOM: our own imports.
+ i "github.com/cockroachdb/redact/interfaces"
+ b "github.com/cockroachdb/redact/internal/buffer"
+ m "github.com/cockroachdb/redact/internal/markers"
+ w "github.com/cockroachdb/redact/internal/redact"
+ "github.com/cockroachdb/redact/internal/rfmt/fmtsort"
)
// Strings for use with buffer.WriteString.
@@ -51,7 +62,8 @@
// The implementation controls how State and rune are interpreted,
// and may call Sprint(f) or Fprint(f) etc. to generate its output.
type Formatter interface {
- Format(f State, verb rune)
+ // CUSTOM: refer to the original type, not the one defined here.
+ Format(f origFmt.State, verb rune)
}
// Stringer is implemented by any value that has a String method,
@@ -71,40 +83,34 @@
GoString() string
}
-// Use simple []byte instead of bytes.Buffer to avoid large dependency.
-type buffer []byte
+// CUSTOM: smarter buffer.
+type buffer struct {
+ b.Buffer
+}
func (b *buffer) write(p []byte) {
- *b = append(*b, p...)
+ b.Write(p)
}
func (b *buffer) writeString(s string) {
- *b = append(*b, s...)
+ b.WriteString(s)
}
func (b *buffer) writeByte(c byte) {
- *b = append(*b, c)
+ b.WriteByte(c)
}
func (bp *buffer) writeRune(r rune) {
- if r < utf8.RuneSelf {
- *bp = append(*bp, byte(r))
- return
- }
-
- b := *bp
- n := len(b)
- for n+utf8.UTFMax > cap(b) {
- b = append(b, 0)
- }
- w := utf8.EncodeRune(b[n:n+utf8.UTFMax], r)
- *bp = b[:n+w]
+ bp.WriteRune(r)
}
// pp is used to store a printer's state and is reused with sync.Pool to avoid allocations.
type pp struct {
buf buffer
+ // CUSTOM: override safety in recursive calls.
+ override overrideMode
+
// arg holds the current item, as an interface{}.
arg interface{}
@@ -150,11 +156,11 @@
// to place back in the pool.
//
// See https://golang.org/issue/23199
- if cap(p.buf) > 64<<10 {
+ if p.buf.Cap() > 64<<10 {
return
}
- p.buf = p.buf[:0]
+ p.buf.Reset()
p.arg = nil
p.value = reflect.Value{}
p.wrappedErr = nil
@@ -183,14 +189,16 @@
// Implement Write so we can call Fprintf on a pp (through State), for
// recursive use in custom verbs.
-func (p *pp) Write(b []byte) (ret int, err error) {
- p.buf.write(b)
- return len(b), nil
+func (p *pp) Write(bs []byte) (ret int, err error) {
+ defer p.startUnsafe().restore()
+ p.buf.write(bs)
+ return len(bs), nil
}
// Implement WriteString so that we can call io.WriteString
// on a pp (through state), for efficiency.
func (p *pp) WriteString(s string) (ret int, err error) {
+ defer p.startUnsafe().restore()
p.buf.writeString(s)
return len(s), nil
}
@@ -202,7 +210,7 @@
func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) {
p := newPrinter()
p.doPrintf(format, a)
- n, err = w.Write(p.buf)
+ n, err = w.Write([]byte(p.buf.TakeRedactableBytes()))
p.free()
return
}
@@ -214,10 +222,10 @@
}
// Sprintf formats according to a format specifier and returns the resulting string.
-func Sprintf(format string, a ...interface{}) string {
+func Sprintf(format string, a ...interface{}) m.RedactableString {
p := newPrinter()
p.doPrintf(format, a)
- s := string(p.buf)
+ s := p.buf.TakeRedactableString()
p.free()
return s
}
@@ -230,7 +238,7 @@
func Fprint(w io.Writer, a ...interface{}) (n int, err error) {
p := newPrinter()
p.doPrint(a)
- n, err = w.Write(p.buf)
+ n, err = w.Write([]byte(p.buf.TakeRedactableBytes()))
p.free()
return
}
@@ -244,10 +252,10 @@
// Sprint formats using the default formats for its operands and returns the resulting string.
// Spaces are added between operands when neither is a string.
-func Sprint(a ...interface{}) string {
+func Sprint(a ...interface{}) m.RedactableString {
p := newPrinter()
p.doPrint(a)
- s := string(p.buf)
+ s := p.buf.TakeRedactableString()
p.free()
return s
}
@@ -262,7 +270,7 @@
func Fprintln(w io.Writer, a ...interface{}) (n int, err error) {
p := newPrinter()
p.doPrintln(a)
- n, err = w.Write(p.buf)
+ n, err = w.Write([]byte(p.buf.TakeRedactableBytes()))
p.free()
return
}
@@ -276,10 +284,10 @@
// Sprintln formats using the default formats for its operands and returns the resulting string.
// Spaces are always added between operands and a newline is appended.
-func Sprintln(a ...interface{}) string {
+func Sprintln(a ...interface{}) m.RedactableString {
p := newPrinter()
p.doPrintln(a)
- s := string(p.buf)
+ s := p.buf.TakeRedactableString()
p.free()
return s
}
@@ -351,6 +359,7 @@
func (p *pp) fmtBool(v bool, verb rune) {
switch verb {
case 't', 'v':
+ defer p.startUnsafe().restore()
p.fmt.fmtBoolean(v)
default:
p.badVerb(verb)
@@ -362,6 +371,7 @@
func (p *pp) fmt0x64(v uint64, leading0x bool) {
sharp := p.fmt.sharp
p.fmt.sharp = leading0x
+ defer p.startUnsafe().restore()
p.fmt.fmtInteger(v, 16, unsigned, 'v', ldigits)
p.fmt.sharp = sharp
}
@@ -373,23 +383,32 @@
if p.fmt.sharpV && !isSigned {
p.fmt0x64(v, true)
} else {
+ defer p.startUnsafe().restore()
p.fmt.fmtInteger(v, 10, isSigned, verb, ldigits)
}
case 'd':
+ defer p.startUnsafe().restore()
p.fmt.fmtInteger(v, 10, isSigned, verb, ldigits)
case 'b':
+ defer p.startUnsafe().restore()
p.fmt.fmtInteger(v, 2, isSigned, verb, ldigits)
case 'o', 'O':
+ defer p.startUnsafe().restore()
p.fmt.fmtInteger(v, 8, isSigned, verb, ldigits)
case 'x':
+ defer p.startUnsafe().restore()
p.fmt.fmtInteger(v, 16, isSigned, verb, ldigits)
case 'X':
+ defer p.startUnsafe().restore()
p.fmt.fmtInteger(v, 16, isSigned, verb, udigits)
case 'c':
+ defer p.startUnsafe().restore()
p.fmt.fmtC(v)
case 'q':
+ defer p.startUnsafe().restore()
p.fmt.fmtQc(v)
case 'U':
+ defer p.startUnsafe().restore()
p.fmt.fmtUnicode(v)
default:
p.badVerb(verb)
@@ -401,12 +420,16 @@
func (p *pp) fmtFloat(v float64, size int, verb rune) {
switch verb {
case 'v':
+ defer p.startUnsafe().restore()
p.fmt.fmtFloat(v, size, 'g', -1)
case 'b', 'g', 'G', 'x', 'X':
+ defer p.startUnsafe().restore()
p.fmt.fmtFloat(v, size, verb, -1)
case 'f', 'e', 'E':
+ defer p.startUnsafe().restore()
p.fmt.fmtFloat(v, size, verb, 6)
case 'F':
+ defer p.startUnsafe().restore()
p.fmt.fmtFloat(v, size, 'f', 6)
default:
p.badVerb(verb)
@@ -437,18 +460,23 @@
func (p *pp) fmtString(v string, verb rune) {
switch verb {
case 'v':
+ defer p.startUnsafe().restore()
if p.fmt.sharpV {
p.fmt.fmtQ(v)
} else {
p.fmt.fmtS(v)
}
case 's':
+ defer p.startUnsafe().restore()
p.fmt.fmtS(v)
case 'x':
+ defer p.startUnsafe().restore()
p.fmt.fmtSx(v, ldigits)
case 'X':
+ defer p.startUnsafe().restore()
p.fmt.fmtSx(v, udigits)
case 'q':
+ defer p.startUnsafe().restore()
p.fmt.fmtQ(v)
default:
p.badVerb(verb)
@@ -478,17 +506,24 @@
if i > 0 {
p.buf.writeByte(' ')
}
- p.fmt.fmtInteger(uint64(c), 10, unsigned, verb, ldigits)
+ func(p *pp, c byte, verb rune) {
+ defer p.startUnsafe().restore()
+ p.fmt.fmtInteger(uint64(c), 10, unsigned, verb, ldigits)
+ }(p, c, verb)
}
p.buf.writeByte(']')
}
case 's':
+ defer p.startUnsafe().restore()
p.fmt.fmtBs(v)
case 'x':
+ defer p.startUnsafe().restore()
p.fmt.fmtBx(v, ldigits)
case 'X':
+ defer p.startUnsafe().restore()
p.fmt.fmtBx(v, udigits)
case 'q':
+ defer p.startUnsafe().restore()
p.fmt.fmtQ(string(v))
default:
p.printValue(reflect.ValueOf(v), verb, 0)
@@ -519,6 +554,7 @@
p.buf.writeByte(')')
} else {
if u == 0 {
+ defer p.startUnsafe().restore()
p.fmt.padString(nilAngleString)
} else {
p.fmt0x64(uint64(u), !p.fmt.sharp)
@@ -586,10 +622,37 @@
verb = 'v'
}
+ if p.override != overrideUnsafe {
+ switch v := p.arg.(type) {
+ case i.SafeFormatter:
+ handled = true
+ defer p.catchPanic(p.arg, verb, "SafeFormat")
+ v.SafeFormat(p, verb)
+ return
+
+ case i.SafeMessager:
+ handled = true
+ defer p.catchPanic(p.arg, verb, "SafeMessager")
+ defer p.startSafeOverride().restore()
+ p.fmtString(v.SafeMessage(), verb)
+ return
+
+ case error:
+ if redactErrorFn != nil {
+ handled = true
+ defer p.catchPanic(p.arg, verb, "SafeFormatter")
+ redactErrorFn(v, p, verb)
+ return
+ }
+ }
+ }
+
// Is it a Formatter?
if formatter, ok := p.arg.(Formatter); ok {
handled = true
defer p.catchPanic(p.arg, verb, "Format")
+ // The mode is implicit: the Format() call will
+ // use p.Write() which already sets the mode.
formatter.Format(p, verb)
return
}
@@ -600,6 +663,7 @@
handled = true
defer p.catchPanic(p.arg, verb, "GoString")
// Print the result of GoString unadorned.
+ defer p.startUnsafe().restore()
p.fmt.fmtS(stringer.GoString())
return
}
@@ -632,6 +696,21 @@
}
func (p *pp) printArg(arg interface{}, verb rune) {
+ t := reflect.TypeOf(arg)
+ if safeTypeRegistry[t] {
+ defer p.startSafeOverride().restore()
+ } else if t == safeWrapperType {
+ defer p.startSafeOverride().restore()
+ arg = arg.(w.SafeWrapper).GetValue()
+ } else if t == unsafeWrapperType {
+ defer p.startUnsafeOverride().restore()
+ arg = arg.(w.UnsafeWrap).GetValue()
+ }
+
+ if _, ok := arg.(i.SafeValue); ok {
+ defer p.startSafeOverride().restore()
+ }
+
p.arg = arg
p.value = reflect.Value{}
@@ -697,13 +776,35 @@
case reflect.Value:
// Handle extractable values with special methods
// since printValue does not handle them at depth 0.
- if f.IsValid() && f.CanInterface() {
- p.arg = f.Interface()
- if p.handleMethods(verb) {
+ if f.IsValid() {
+ t := f.Type()
+ if p.handleSpecialValues(f, t, verb, 0) {
return
}
+
+ if safeTypeRegistry[t] {
+ defer p.startSafeOverride().restore()
+ }
+
+ if f.CanInterface() {
+ p.arg = f.Interface()
+ if _, ok := p.arg.(i.SafeValue); ok {
+ defer p.startSafeOverride().restore()
+ }
+ if p.handleMethods(verb) {
+ return
+ }
+ }
}
p.printValue(f, verb, 0)
+ case m.RedactableString:
+ defer p.startPreRedactable().restore()
+ p.buf.WriteString(string(f))
+ return
+ case m.RedactableBytes:
+ defer p.startPreRedactable().restore()
+ p.buf.Write([]byte(f))
+ return
default:
// If the type is not simple, it might have methods.
if !p.handleMethods(verb) {
@@ -718,11 +819,25 @@
// It does not handle 'p' and 'T' verbs because these should have been already handled by printArg.
func (p *pp) printValue(value reflect.Value, verb rune, depth int) {
// Handle values with special methods if not already handled by printArg (depth == 0).
- if depth > 0 && value.IsValid() && value.CanInterface() {
- p.arg = value.Interface()
- if p.handleMethods(verb) {
+ if depth > 0 && value.IsValid() {
+ t := value.Type()
+ if p.handleSpecialValues(value, t, verb, depth) {
return
}
+
+ if safeTypeRegistry[t] {
+ defer p.startSafeOverride().restore()
+ }
+
+ if value.CanInterface() {
+ p.arg = value.Interface()
+ if _, ok := p.arg.(i.SafeValue); ok {
+ defer p.startSafeOverride().restore()
+ }
+ if p.handleMethods(verb) {
+ return
+ }
+ }
}
p.arg = nil
p.value = value
@@ -946,7 +1061,9 @@
// argNumber returns the next argument to evaluate, which is either the value of the passed-in
// argNum or the value of the bracketed integer that begins format[i:]. It also returns
// the new value of i, that is, the index of the next byte of the format to process.
-func (p *pp) argNumber(argNum int, format string, i int, numArgs int) (newArgNum, newi int, found bool) {
+func (p *pp) argNumber(
+ argNum int, format string, i int, numArgs int,
+) (newArgNum, newi int, found bool) {
if len(format) <= i || format[i] != '[' {
return argNum, i, false
}
@@ -972,6 +1089,7 @@
}
func (p *pp) doPrintf(format string, a []interface{}) {
+ p.buf.SetMode(b.SafeEscaped)
end := len(format)
argNum := 0 // we process one argument per non-trivial format
afterIndex := false // previous item in format was an index like [3].
@@ -1147,6 +1265,7 @@
}
func (p *pp) doPrint(a []interface{}) {
+ p.buf.SetMode(b.SafeEscaped)
prevString := false
for argNum, arg := range a {
isString := arg != nil && reflect.TypeOf(arg).Kind() == reflect.String
@@ -1162,6 +1281,7 @@
// doPrintln is like doPrint but always adds a space between arguments
// and a newline after the last argument.
func (p *pp) doPrintln(a []interface{}) {
+ p.buf.SetMode(b.SafeEscaped)
for argNum, arg := range a {
if argNum > 0 {
p.buf.writeByte(' ')

View File

@@ -0,0 +1,85 @@
// Copyright 2021 The Cockroach Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
// implied. See the License for the specific language governing
// permissions and limitations under the License.
package rfmt
import i "github.com/cockroachdb/redact/interfaces"
// SafeString implements SafePrinter.
func (p *pp) SafeString(s i.SafeString) {
defer p.startSafeOverride().restore()
p.buf.WriteString(string(s))
}
// SafeInt implements SafePrinter.
func (p *pp) SafeInt(s i.SafeInt) {
defer p.startSafeOverride().restore()
p.fmtInteger(uint64(s), signed, 'd')
}
// SafeUint implements SafePrinter.
func (p *pp) SafeUint(s i.SafeUint) {
defer p.startSafeOverride().restore()
p.fmtInteger(uint64(s), unsigned, 'd')
}
// SafeFloat implements SafePrinter.
func (p *pp) SafeFloat(s i.SafeFloat) {
defer p.startSafeOverride().restore()
p.fmtFloat(float64(s), 64, 'v')
}
// SafeRune implements SafePrinter.
func (p *pp) SafeRune(r i.SafeRune) {
defer p.startSafeOverride().restore()
p.buf.WriteRune(rune(r))
}
func (p *pp) Print(args ...interface{}) {
defer p.buf.SetMode(p.buf.GetMode())
np := newPrinter()
np.buf = p.buf
np.doPrint(args)
p.buf = np.buf
np.buf = buffer{}
np.free()
}
func (p *pp) Printf(format string, arg ...interface{}) {
defer p.buf.SetMode(p.buf.GetMode())
np := newPrinter()
np.buf = p.buf
np.doPrintf(format, arg)
p.buf = np.buf
np.buf = buffer{}
np.free()
}
func (p *pp) UnsafeString(s string) {
defer p.startUnsafe().restore()
_, _ = p.buf.WriteString(s)
}
func (p *pp) UnsafeByte(bb byte) {
defer p.startUnsafe().restore()
_ = p.buf.WriteByte(bb)
}
func (p *pp) UnsafeBytes(bs []byte) {
defer p.startUnsafe().restore()
_, _ = p.buf.Write(bs)
}
func (p *pp) UnsafeRune(r rune) {
defer p.startUnsafe().restore()
_ = p.buf.WriteRune(r)
}

View File

@@ -5,11 +5,8 @@
#
set -euxo pipefail
(
echo "// Code generated from the Go standard library. DO NOT EDIT"
echo "// GENERATED FILE DO NOT EDIT"
cat $GOROOT/src/fmt/format.go
) >format.go
cp $GOROOT/src/fmt/format.go format.go
patch -p0 <format.go.diff
cp $GOROOT/src/fmt/print.go print.go
patch -p0 <print.go.diff

View File

@@ -12,9 +12,13 @@
// implied. See the License for the specific language governing
// permissions and limitations under the License.
package redact
package rfmt
import "reflect"
import (
"reflect"
i "github.com/cockroachdb/redact/interfaces"
)
// RegisterSafeType registers a data type to always be considered safe
// during the production of redactable strings.
@@ -29,3 +33,14 @@ var safeTypeRegistry = map[reflect.Type]bool{}
func isSafeValue(a interface{}) bool {
return safeTypeRegistry[reflect.TypeOf(a)]
}
// redactErrorFn can be injected from an error library
// to render error objects safely.
var redactErrorFn func(err error, p i.SafePrinter, verb rune)
// RegisterRedactErrorFn registers an error redaction function for use
// during automatic redaction by this package.
// Provided e.g. by cockroachdb/errors.
func RegisterRedactErrorFn(fn func(err error, p i.SafePrinter, verb rune)) {
redactErrorFn = fn
}

View File

@@ -1,40 +0,0 @@
// Copyright 2020 The Cockroach Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
// implied. See the License for the specific language governing
// permissions and limitations under the License.
package redact
import "regexp"
const startRedactable = '‹'
const startRedactableS = string(startRedactable)
var startRedactableBytes = []byte(startRedactableS)
const endRedactable = '›'
const endRedactableS = string(endRedactable)
var endRedactableBytes = []byte(endRedactableS)
const escapeMark = '?'
const escapeMarkS = string(escapeMark)
var escapeBytes = []byte(escapeMarkS)
const redactedS = startRedactableS + "×" + endRedactableS
var redactedBytes = []byte(redactedS)
var reStripSensitive = regexp.MustCompile(startRedactableS + "[^" + startRedactableS + endRedactableS + "]*" + endRedactableS)
var reStripMarkers = regexp.MustCompile("[" + startRedactableS + endRedactableS + "]")

View File

@@ -1,217 +0,0 @@
// Copyright 2020 The Cockroach Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
// implied. See the License for the specific language governing
// permissions and limitations under the License.
package redact
import (
"bytes"
"fmt"
"io"
)
// escapeState abstracts on top of fmt.State and ensures that call
// calls to Write() enclose the writtne bytes between unsafe markers.
type escapeState struct {
fmt.State
w escapeWriter
}
var _ fmt.State = (*escapeState)(nil)
func makeEscapeState(s fmt.State, buf *bytes.Buffer) escapeState {
e := escapeState{State: s}
e.w = escapeWriter{w: buf, enclose: true}
return e
}
// Write is part of the fmt.State interface and implements the io.Writer interface.
func (p *escapeState) Write(b []byte) (int, error) {
return p.w.Write(b)
}
// escapeWriter abstracts on top of io.Writer and ensures that all
// calls to Write() escape markers.
// Also, final spaces and newlines are stripped if strip is set.
// Also, the overall result is enclosed inside redaction markers
// if enclose is true.
type escapeWriter struct {
w io.Writer
enclose bool
strip bool
}
var _ io.Writer = (*escapeWriter)(nil)
// Write implements the io.Writer interface.
func (p *escapeWriter) Write(b []byte) (int, error) {
st := escapeResult{0, nil}
if p.strip {
// Trim final newlines/spaces, for convenience.
end := len(b)
for i := end - 1; i >= 0; i-- {
if b[i] == '\n' || b[i] == ' ' {
end = i
} else {
break
}
}
b = b[:end]
}
// Here we could choose to omit the output
// entirely if there was nothing but empty space:
// if len(b) == 0 { return 0, nil }
// Note: we use len(...RedactableS) and not len(...RedactableBytes)
// because the ...S variant is a compile-time constant so this
// accelerates the loops below.
start, ls := startRedactableBytes, len(startRedactableS)
end, le := endRedactableBytes, len(endRedactableS)
escape := escapeBytes
if p.enclose {
st = p.doWrite(start, st, false)
}
// Now write the string.
// k is the index in b up to (and excluding) the byte which we've
// already copied into the output.
k := 0
for i := 0; i < len(b); i++ {
if b[i] == '\n' && p.enclose {
// Avoid enclosing newline characters inside redaction markers.
// This is important when redact is used to render errors, where
// sub-strings split at newline characters are rendered
// separately.
st = p.doWrite(b[k:i], st, true)
st = p.doWrite(end, st, false)
// Advance to the last newline character. We want to forward
// them all in a single call to doWrite, for performance.
lastNewLine := i
for b[lastNewLine] == '\n' && lastNewLine < len(b) {
lastNewLine++
}
st = p.doWrite(b[i:lastNewLine], st, true)
st = p.doWrite(start, st, false)
// Advance the counters by the number of newline characters.
k = lastNewLine
i = lastNewLine - 1 /* -1 because we have i++ at the end of every iteration */
} else {
// Ensure that occurrences of the delimiter inside the string get
// escaped.
// Reminder: ls and le are likely greater than 1, as we are scanning
// utf-8 encoded delimiters (the utf-8 encoding is multibyte).
if i+ls <= len(b) && bytes.Equal(b[i:i+ls], start) {
st = p.doWrite(b[k:i], st, true)
st = p.doWrite(escape, st, false)
// Advance the counters by the length (in bytes) of the delimiter.
st.l += ls
k = i + ls
i += ls - 1 /* -1 because we have i++ at the end of every iteration */
} else if i+le <= len(b) && bytes.Equal(b[i:i+le], end) {
st = p.doWrite(b[k:i], st, true)
st = p.doWrite(escape, st, false)
// Advance the counters by the length (in bytes) of the delimiter.
st.l += le
k = i + le
i += le - 1 /* -1 because we have i++ at the end of every iteration */
}
}
}
st = p.doWrite(b[k:], st, true)
if p.enclose {
st = p.doWrite(end, st, false)
}
return st.l, st.err
}
type escapeResult struct {
l int
err error
}
func (p *escapeWriter) doWrite(b []byte, st escapeResult, count bool) escapeResult {
if st.err != nil {
// An error was encountered previously.
// No-op.
return st
}
sz, err := p.w.Write(b)
if count {
st.l += sz
}
st.err = err
return st
}
// internalEscapeBytes escapes redaction markers in the provided buf
// starting at the location startLoc.
// The bytes before startLoc are considered safe (already escaped).
func internalEscapeBytes(b []byte, startLoc int) (res []byte) {
// Note: we use len(...RedactableS) and not len(...RedactableBytes)
// because the ...S variant is a compile-time constant so this
// accelerates the loops below.
start, ls := startRedactableBytes, len(startRedactableS)
end, le := endRedactableBytes, len(endRedactableS)
escape := escapeBytes
// res is the output slice. In the common case where there is
// nothing to escape, the input slice is returned directly
// and no allocation takes place.
res = b
// copied is true if and only if `res` is a copy of `b`. It only
// turns to true if the loop below finds something to escape.
copied := false
// k is the index in b up to (and excluding) the byte which we've
// already copied into res (if copied=true).
k := 0
for i := startLoc; i < len(b); i++ {
// Ensure that occurrences of the delimiter inside the string get
// escaped.
// Reminder: ls and le are likely greater than 1, as we are scanning
// utf-8 encoded delimiters (the utf-8 encoding is multibyte).
if i+ls <= len(b) && bytes.Equal(b[i:i+ls], start) {
if !copied {
// We only allocate an output slice when we know we definitely
// need it.
res = make([]byte, 0, len(b)+len(escape))
copied = true
}
res = append(res, b[k:i]...)
res = append(res, escape...)
// Advance the counters by the length (in bytes) of the delimiter.
k = i + ls
i += ls - 1 /* -1 because we have i++ at the end of every iteration */
} else if i+le <= len(b) && bytes.Equal(b[i:i+le], end) {
if !copied {
// See the comment above about res allocation.
res = make([]byte, 0, len(b)+len(escape))
copied = true
}
res = append(res, b[k:i]...)
res = append(res, escape...)
// Advance the counters by the length (in bytes) of the delimiter.
k = i + le
i += le - 1 /* -1 because we have i++ at the end of every iteration */
}
}
if copied {
res = append(res, b[k:]...)
}
return
}

View File

@@ -1,264 +0,0 @@
// Copyright 2020 The Cockroach Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
// implied. See the License for the specific language governing
// permissions and limitations under the License.
package redact
import (
"fmt"
internalFmt "github.com/cockroachdb/redact/internal"
)
// printArgFn is the hook injected into the standard fmt logic
// by the printer functions in markers_print.go.
func printArgFn(p *internalFmt.InternalPrinter, arg interface{}, verb rune) (newState int) {
redactLastWrites(p)
switch verb {
case 'T':
// If the value was wrapped, reveal its original type. Anything else is not very useful.
switch v := arg.(type) {
case safeWrapper:
arg = v.a
case unsafeWrap:
arg = v.a
}
// Shortcut: %T is always safe to print as-is.
internalFmt.PrintArg(p, arg, verb)
return len(internalFmt.Buf(p))
case 'p':
// Printing a pointer via %p is handled as special case in printf,
// so we need a special case here too. The other cases of
// printing a pointer via %v / %d %x etc is handled by the common path.
switch v := arg.(type) {
case safeWrapper:
// If the value was meant to be safe, then print it as-is.
internalFmt.PrintArg(p, v.a, verb)
return len(internalFmt.Buf(p))
case unsafeWrap:
// If it's been wrapped, unwrap it. This helps preserve the
// original pointer value in the output.
arg = v.a
}
// Now perform an unsafe print: we are assuming that the pointer
// representation by the fmt.printf code does not contain
// redaction markers, and go a short route. If that assumption did
// not hold (or is invalidated by changes upstream after this
// comment is written), this code should be changed to use the
// escapeWriter instead.
internalFmt.Append(p, startRedactableBytes)
internalFmt.PrintArg(p, arg, verb)
internalFmt.Append(p, endRedactableBytes)
return len(internalFmt.Buf(p))
}
// nil arguments are printed as-is. Note: a nil argument under
// interface{} is not the same as a nil pointer passed via a pointer
// of a concrete type. The latter kind of nil goes through
// redaction as usual, because it may have its own custom Format() method.
if arg == nil {
internalFmt.PrintArg(p, arg, verb)
return len(internalFmt.Buf(p))
}
// RedactableBytes/RedactableString are already formatted as
// redactable. Include them as-is.
//
// NB: keep this logic synchronized with
// (RedactableString/Bytes).SafeFormat().
switch v := arg.(type) {
case RedactableString:
internalFmt.Append(p, []byte(v))
return len(internalFmt.Buf(p))
case RedactableBytes:
internalFmt.Append(p, []byte(v))
return len(internalFmt.Buf(p))
}
arg = annotateArg(arg, internalFmt.CollectingError(p))
internalFmt.PrintArg(p, arg, verb)
return len(internalFmt.Buf(p))
}
// redactLastWrites escapes any markers that were added by the
// internals of the printf functions, for example
// if markers were present in the format string.
func redactLastWrites(p *internalFmt.InternalPrinter) {
state := internalFmt.GetState(p)
newBuf := internalEscapeBytes(internalFmt.Buf(p), state)
internalFmt.SetState(p, newBuf)
}
// annotateArg wraps the arguments to one of the print functions with
// an indirect formatter which ensures that redaction markers inside
// the representation of the object are escaped, and optionally
// encloses the result of the display between redaction markers.
//
// collectingError is true iff we are in the context of
// HelperForErrorf, where we want the %w verb to work properly. This
// adds a little overhead to the processing, but this is OK because
// typically the error path is not perf-critical.
func annotateArg(arg interface{}, collectingError bool) interface{} {
var newArg fmt.Formatter
err, isError := arg.(error)
switch v := arg.(type) {
case SafeFormatter:
// calls to Format() by fmt.Print will be redirected to
// v.SafeFormat(). This delegates the task of adding markers to
// the object itself.
newArg = &redactFormatRedirect{
func(p SafePrinter, verb rune) { v.SafeFormat(p, verb) },
}
case SafeValue:
// calls to Format() by fmt.Print will be redirected to a
// display of v without redaction markers.
//
// Note that we can't let the value be displayed as-is because
// we must prevent any marker inside the value from leaking into
// the result. (We want to avoid mismatched markers.)
newArg = &escapeArg{arg: arg, enclose: false}
case SafeMessager:
// Obsolete interface.
// TODO(knz): Remove this.
newArg = &escapeArg{arg: v.SafeMessage(), enclose: false}
default:
if isError && redactErrorFn != nil {
// We place this case after the other cases above, in case
// the error object knows how to print itself safely already.
newArg = &redactFormatRedirect{
func(p SafePrinter, verb rune) { redactErrorFn(err, p, verb) },
}
} else {
// calls to Format() by fmt.Print will be redirected to a
// display of v within redaction markers if the type is
// considered unsafe, without markers otherwise. In any case,
// occurrences of delimiters within are escaped.
newArg = &escapeArg{arg: v, enclose: !isSafeValue(v)}
}
}
if isError && collectingError {
// Ensure the arg still implements the `error` interface for
// detection by the handling of %w, while also implementing
// fmt.Formatter to forward the implementation to the objects
// constructed above.
newArg = &makeError{err: err, arg: newArg}
}
return newArg
}
type makeError struct {
err error
arg fmt.Formatter
}
// Error implements error.
func (m *makeError) Error() string { return m.err.Error() }
// Format implements fmt.Formatter.
func (m *makeError) Format(f fmt.State, verb rune) { m.arg.Format(f, verb) }
// redactFormatRedirect wraps a safe print callback and uses it to
// implement fmt.Formatter.
type redactFormatRedirect struct {
printFn func(p SafePrinter, verb rune)
}
// Format implements fmt.Formatter.
func (r *redactFormatRedirect) Format(s fmt.State, verb rune) {
defer func() {
if p := recover(); p != nil {
e := escapeWriter{w: s}
fmt.Fprintf(&e, "%%!%c(PANIC=SafeFormatter method: %v)", verb, p)
}
}()
p := &printer{}
p.escapeState = makeEscapeState(s, &p.buf)
r.printFn(p, verb)
_, _ = s.Write(p.buf.Bytes())
}
// passthrough passes a pre-formatted string through.
type passthrough struct{ arg []byte }
// Format implements fmt.Formatter.
func (p *passthrough) Format(s fmt.State, _ rune) {
_, _ = s.Write(p.arg)
}
// escapeArg wraps an arbitrary value and ensures that any occurrence
// of the redaction markers in its representation are escaped.
//
// The result of printing out the value is enclosed within markers or
// not depending on the value of the enclose bool.
type escapeArg struct {
arg interface{}
enclose bool
}
func (r *escapeArg) Format(s fmt.State, verb rune) {
switch t := r.arg.(type) {
case fmt.Formatter:
// This is a special case from the default case below, which
// allows a shortcut through the layers of the fmt package.
p := &escapeState{
State: s,
w: escapeWriter{
w: s,
enclose: r.enclose,
strip: r.enclose,
}}
defer func() {
if recovered := recover(); recovered != nil {
fmt.Fprintf(p, "%%!%c(PANIC=Format method: %v)", verb, recovered)
}
}()
t.Format(p, verb)
default:
// TODO(knz): It would be possible to implement struct formatting
// with conditional redaction based on field tag annotations here.
p := &escapeWriter{w: s, enclose: r.enclose, strip: r.enclose}
reproducePrintf(p, s, verb, r.arg)
}
}
// printerfn is a helper struct for use by Sprintfn.
type printerfn struct {
fn func(SafePrinter)
}
// SafeFormat implements the SafeFormatter interface.
func (p printerfn) SafeFormat(w SafePrinter, _ rune) {
p.fn(w)
}
// redactErrorFn can be injected from an error library
// to render error objects safely.
var redactErrorFn func(err error, p SafePrinter, verb rune)
// RegisterRedactErrorFn registers an error redaction function for use
// during automatic redaction by this package.
// Provided e.g. by cockroachdb/errors.
func RegisterRedactErrorFn(fn func(err error, p SafePrinter, verb rune)) {
redactErrorFn = fn
}

View File

@@ -1,88 +0,0 @@
// Copyright 2020 The Cockroach Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
// implied. See the License for the specific language governing
// permissions and limitations under the License.
package redact
import (
"bytes"
"fmt"
"unicode/utf8"
)
// printer implements SafePrinter.
// This is the machinery for the Print() functions offered
// by this package.
type printer struct {
escapeState
buf bytes.Buffer
}
var _ fmt.State = (*printer)(nil)
var _ SafeWriter = (*printer)(nil)
// Print is part of the SafeWriter interface.
func (b *printer) Print(args ...interface{}) {
_, _ = Fprint(&b.buf, args...)
}
// Printf is part of the SafeWriter interface.
func (b *printer) Printf(format string, args ...interface{}) {
_, _ = Fprintf(&b.buf, format, args...)
}
// SafeString is part of the SafeWriter interface.
func (b *printer) SafeString(s SafeString) {
w := escapeWriter{w: &b.buf, enclose: false}
_, _ = w.Write([]byte(s))
}
// SafeRune is part of the SafeWriter interface.
func (b *printer) SafeRune(s SafeRune) {
if s == startRedactable || s == endRedactable {
s = escapeMark
}
_, _ = b.buf.WriteRune(rune(s))
}
// UnsafeString is part of the SafeWriter interface.
func (b *printer) UnsafeString(s string) {
w := escapeWriter{w: &b.buf, enclose: true, strip: true}
_, _ = w.Write([]byte(s))
}
// UnsafeRune is part of the SafeWriter interface.
func (b *printer) UnsafeRune(s rune) {
_, _ = b.buf.WriteRune(startRedactable)
b.SafeRune(SafeRune(s))
_, _ = b.buf.WriteRune(endRedactable)
}
// UnsafeByte is part of the SafeWriter interface.
func (b *printer) UnsafeByte(s byte) {
_, _ = b.buf.WriteRune(startRedactable)
if s >= utf8.RuneSelf ||
s == startRedactableBytes[0] || s == endRedactableBytes[0] {
// Unsafe byte. Escape it.
_, _ = b.buf.Write(escapeBytes)
} else {
_ = b.buf.WriteByte(s)
}
_, _ = b.buf.WriteRune(endRedactable)
}
// UnsafeBytes is part of the SafeWriter interface.
func (b *printer) UnsafeBytes(s []byte) {
w := escapeWriter{w: &b.buf, enclose: true, strip: true}
_, _ = w.Write(s)
}

View File

@@ -17,7 +17,7 @@ package redact
import (
"io"
internalFmt "github.com/cockroachdb/redact/internal"
"github.com/cockroachdb/redact/internal/rfmt"
)
// Sprint prints out the arguments and encloses unsafe bits
@@ -27,13 +27,7 @@ import (
// If a RedactableString or RedactableBytes argument is passed,
// it is reproduced as-is without escaping.
func Sprint(args ...interface{}) RedactableString {
p := internalFmt.NewInternalPrinter()
internalFmt.SetHook(p, printArgFn)
internalFmt.DoPrint(p, args)
redactLastWrites(p)
s := RedactableString(internalFmt.Buf(p))
internalFmt.Free(p)
return s
return rfmt.Sprint(args...)
}
// Sprintf formats the arguments and encloses unsafe bits
@@ -44,13 +38,7 @@ func Sprint(args ...interface{}) RedactableString {
// is responsible to ensure that the markers are not present
// in the format string.
func Sprintf(format string, args ...interface{}) RedactableString {
p := internalFmt.NewInternalPrinter()
internalFmt.SetHook(p, printArgFn)
internalFmt.DoPrintf(p, format, args)
redactLastWrites(p)
s := RedactableString(internalFmt.Buf(p))
internalFmt.Free(p)
return s
return rfmt.Sprintf(format, args...)
}
// HelperForErrorf is a helper to implement a redaction-aware
@@ -63,24 +51,13 @@ func Sprintf(format string, args ...interface{}) RedactableString {
// Note: This function only works if an error redaction function
// has been injected with RegisterRedactErrorFn().
func HelperForErrorf(format string, args ...interface{}) (RedactableString, error) {
p := internalFmt.NewInternalPrinter()
internalFmt.SetCollectError(p)
internalFmt.SetHook(p, printArgFn)
internalFmt.DoPrintf(p, format, args)
redactLastWrites(p)
s := RedactableString(internalFmt.Buf(p))
e := internalFmt.WrappedError(p)
internalFmt.Free(p)
if m, ok := e.(*makeError); ok {
e = m.err
}
return s, e
return rfmt.HelperForErrorf(format, args...)
}
// Sprintfn produces a RedactableString using the provided
// SafeFormat-alike function.
func Sprintfn(printer func(w SafePrinter)) RedactableString {
return Sprint(printerfn{printer})
return rfmt.Sprintfn(printer)
}
// StringWithoutMarkers formats the provided SafeFormatter and strips
@@ -100,23 +77,11 @@ func StringWithoutMarkers(f SafeFormatter) string {
// Fprint is like Sprint but outputs the redactable
// string to the provided Writer.
func Fprint(w io.Writer, args ...interface{}) (n int, err error) {
p := internalFmt.NewInternalPrinter()
internalFmt.SetHook(p, printArgFn)
internalFmt.DoPrint(p, args)
redactLastWrites(p)
n, err = w.Write(internalFmt.Buf(p))
internalFmt.Free(p)
return
return rfmt.Fprint(w, args...)
}
// Fprintf is like Sprintf but outputs the redactable string to the
// provided Writer.
func Fprintf(w io.Writer, format string, args ...interface{}) (n int, err error) {
p := internalFmt.NewInternalPrinter()
internalFmt.SetHook(p, printArgFn)
internalFmt.DoPrintf(p, format, args)
redactLastWrites(p)
n, err = w.Write(internalFmt.Buf(p))
internalFmt.Free(p)
return
return rfmt.Fprintf(w, format, args...)
}

62
vendor/github.com/cockroachdb/redact/util.go generated vendored Normal file
View File

@@ -0,0 +1,62 @@
// Copyright 2020 The Cockroach Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
// implied. See the License for the specific language governing
// permissions and limitations under the License.
package redact
import (
"reflect"
"sort"
"github.com/cockroachdb/redact/builder"
)
// JoinTo writes the given slice of values delimited by the provided
// delimiter to the given SafeWriter.
func JoinTo(w SafeWriter, delim RedactableString, values interface{}) {
// Ideally, we'd make the interface clearer: JoinTo is a generic function,
// with a "values" argument of type []T where T is a type variable.
v := reflect.ValueOf(values)
if v.Kind() != reflect.Slice {
// Note a slice: just print the value as-is.
w.Print(values)
}
for i, l := 0, v.Len(); i < l; i++ {
if i > 0 {
w.Print(delim)
}
w.Print(v.Index(i).Interface())
}
}
// Join creates a redactable string from the
// given slice of redactable strings, adjoined
// with the provided delimiter.
func Join(delim RedactableString, s []RedactableString) RedactableString {
var b builder.StringBuilder
JoinTo(&b, delim, s)
return b.RedactableString()
}
// SortStrings sorts the provided slice of redactable strings.
func SortStrings(s []RedactableString) {
sort.Sort(sortableSlice(s))
}
// sortableSlice attaches the methods of sort.Interface to
// []RedactableString, sorting in increasing order.
type sortableSlice []RedactableString
func (p sortableSlice) Len() int { return len(p) }
func (p sortableSlice) Less(i, j int) bool { return p[i] < p[j] }
func (p sortableSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }