Initial commit

This commit is contained in:
eleksir
2025-05-08 18:12:52 +03:00
commit b21e3c84c0
1553 changed files with 649819 additions and 0 deletions

15
vendor/github.com/cockroachdb/redact/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,15 @@
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
# Test binary, built with `go test -c`
*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
# Dependency directories (remove the comment below to include it)
# vendor/

7
vendor/github.com/cockroachdb/redact/.travis.yml generated vendored Normal file
View File

@@ -0,0 +1,7 @@
language: go
go:
- 1.12.x
- 1.13.x
- 1.14.x
- 1.15.x

201
vendor/github.com/cockroachdb/redact/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
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.

9
vendor/github.com/cockroachdb/redact/README.md generated vendored Normal file
View File

@@ -0,0 +1,9 @@
# redact
Utilities to redact Go strings for confidentiality
![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

175
vendor/github.com/cockroachdb/redact/api.go generated vendored Normal file
View File

@@ -0,0 +1,175 @@
// 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"
"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 = i.SafeFormatter
// 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 = 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 = i.SafeMessager
// 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 = i.SafePrinter
// SafeWriter provides helper functions for use in implementations of
// SafeFormatter, to format mixes of safe and unsafe strings.
type SafeWriter = i.SafeWriter
// SafeString represents a string that is not a sensitive value.
type SafeString = i.SafeString
// SafeInt represents an integer that is not a sensitive value.
type SafeInt = i.SafeInt
// SafeUint represents an integer that is not a sensitive value.
type SafeUint = i.SafeUint
// 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 = i.SafeRune
// 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)
}

165
vendor/github.com/cockroachdb/redact/builder/builder.go generated vendored Normal file
View File

@@ -0,0 +1,165 @@
// 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 builder
import (
"fmt"
"io"
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.
//
// It implements io.Writer but marks direct writes as redactable.
// To distinguish safe and unsafe bits, it also implements the SafeWriter
// interface.
type StringBuilder struct {
ib.Buffer
}
var _ fmt.Stringer = StringBuilder{}
var _ fmt.Stringer = (*StringBuilder)(nil)
// SafeFormat implements SafeFormatter.
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.
//
// We don't do this here, keeping in mind that the output
// of a SafeFormat must remain a redactable string.
//
// %x/%X cannot be implemented because they would turn redaction
// markers into hex codes, and the entire result string would
// appear safe for reporting, which would break the semantics
// of this package.
//
// %q cannot be implemented because it replaces non-ASCII characters
// with numeric unicode escapes, which breaks redaction
// markers too.
p.Print(b.RedactableString())
}
var _ i.SafeFormatter = StringBuilder{}
var _ i.SafeFormatter = (*StringBuilder)(nil)
// StringBuilder implements io.Writer.
// Direct Write() calls are considered unsafe.
var _ io.Writer = (*StringBuilder)(nil)
// Write implements the io.Writer interface.
func (b *StringBuilder) Write(s []byte) (int, error) {
b.SetMode(ib.UnsafeEscaped)
return b.Buffer.Write(s)
}
// WriteString shadows the ib.Buffer.WriteString method.
func (b *StringBuilder) WriteString(s string) (int, error) {
b.SetMode(ib.UnsafeEscaped)
return b.Buffer.WriteString(s)
}
// WriteByte shadows the ib.Buffer.WriteByte method.
func (b *StringBuilder) WriteByte(c byte) error {
b.SetMode(ib.UnsafeEscaped)
return b.Buffer.WriteByte(c)
}
// WriteRune shadows the ib.Buffer.WriteRune method.
func (b *StringBuilder) WriteRune(r rune) error {
b.SetMode(ib.UnsafeEscaped)
return b.Buffer.WriteRune(r)
}
// StringBuilder implements SafeWriter.
var _ i.SafeWriter = (*StringBuilder)(nil)
// Print is part of the SafeWriter interface.
func (b *StringBuilder) Print(args ...interface{}) {
b.SetMode(ib.PreRedactable)
_, _ = ifmt.Fprint(&b.Buffer, args...)
}
// Printf is part of the SafeWriter interface.
func (b *StringBuilder) Printf(format string, args ...interface{}) {
b.SetMode(ib.PreRedactable)
_, _ = ifmt.Fprintf(&b.Buffer, format, args...)
}
// SafeString is part of the SafeWriter interface.
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 i.SafeRune) {
b.SetMode(ib.SafeEscaped)
_ = b.Buffer.WriteRune(rune(s))
}
func (b *StringBuilder) SafeByte(s i.SafeByte) {
b.SetMode(ib.SafeEscaped)
_ = b.Buffer.WriteByte(byte(s))
}
func (b *StringBuilder) SafeBytes(s i.SafeBytes) {
b.SetMode(ib.SafeEscaped)
_, _ = b.Buffer.Write([]byte(s))
}
// UnsafeString is part of the SafeWriter interface.
func (b *StringBuilder) UnsafeString(s string) {
b.SetMode(ib.UnsafeEscaped)
_, _ = b.Buffer.WriteString(s)
}
// UnsafeRune is part of the SafeWriter interface.
func (b *StringBuilder) UnsafeRune(s rune) {
b.SetMode(ib.UnsafeEscaped)
_ = b.Buffer.WriteRune(s)
}
// UnsafeByte is part of the SafeWriter interface.
func (b *StringBuilder) UnsafeByte(s byte) {
b.SetMode(ib.UnsafeEscaped)
_ = b.Buffer.WriteByte(s)
}
// UnsafeBytes is part of the SafeWriter interface.
func (b *StringBuilder) UnsafeBytes(s []byte) {
b.SetMode(ib.UnsafeEscaped)
_, _ = b.Buffer.Write(s)
}

51
vendor/github.com/cockroachdb/redact/doc.go generated vendored Normal file
View File

@@ -0,0 +1,51 @@
// 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 provides facilities for separating “safe” and
// “unsafe” pieces of data when logging and constructing error object.
//
// An item is said to be “safe” if it is proven to not contain
// PII or otherwise confidential information that should not escape
// the boundaries of the current system, for example via telemetry
// or crash reporting. Conversely, data is considered “unsafe”
// until/unless it is known to be “safe”.
//
// Example use:
//
// redactable := redact.Sprintf("hello %s", "universe")
//
// // At this point, 'redactable' contains "hello ‹universe›".
//
// // This prints "hello universe":
// fmt.Println(redactable.StripMarkers())
//
// // This reports "hello ‹×›":
// fmt.Println(redactable.Redact())
//
//
// When defining your own custom types, you can define
// a SafeFormat method, implementing the redact.SafeFormatter
// interface in a way you'd otherwise implement fmt.Formatter.
// This is then recognized by this package's API automatically.
//
// Alternatively:
//
// - you can implement the SafeValue interface, which tells the
// redact package to always the default formatting of a type
// as safe and thus not included inside redaction markers.
//
// - you can include a value within redact.Safe() and redact.Unsafe()
// in redact.Sprintf / redact.Fprintf calls, to force
// the omission or inclusion of redaction markers.
package redact

View File

@@ -0,0 +1,167 @@
// 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)
// SafeByte emits a safe byte.
SafeByte(SafeByte)
// SafeBytes emits a safe byte slice.
SafeBytes(SafeBytes)
// 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() {}
// SafeByte represents a byte that is not a sensitive value.
type SafeByte byte
// SafeValue makes SafeByte a SafeValue.
func (SafeByte) SafeValue() {}
// SafeBytes represents a byte slice that is not a sensitive value.
type SafeBytes []byte
// SafeValue makes SafeBytes a SafeValue.
func (SafeBytes) 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,351 @@
// 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 (
"bytes"
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
}
if bytes.HasSuffix(b.buf, m.StartBytes) {
// Special case: remove a trailing open marker and call it a day.
b.buf = b.buf[:len(b.buf)-m.StartLen]
} else {
p, ok := b.tryGrowByReslice(m.EndLen)
if !ok {
p = b.grow(m.EndLen)
}
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() {
if bytes.HasSuffix(b.buf, m.EndBytes) {
// Special case: remove a trailing closing marker and call it a day.
b.buf = b.buf[:len(b.buf)-m.EndLen]
} else {
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, false /* trim */)
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]
}
// clone is used in tests.
func (b *Buffer) clone() *Buffer {
c := *b
c.buf = append([]byte(nil), b.buf...)
return &c
}
// 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,137 @@
// 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.
//
// If strip is set, final newlines and spaces are trimmed from the
// output.
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]...)
// Either add an end marker, or elide a start marker immediately prior.
if bytes.HasSuffix(res, start) {
res = res[:len(res)-ls]
} else {
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

@@ -0,0 +1,88 @@
// 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 fmtforward
import (
"fmt"
"io"
"strconv"
"strings"
)
// 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.
fmt.Fprint(w, arg)
} else {
fmt.Fprintf(w, revFmt, arg)
}
}
// 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.
//
// 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) {
plus, minus, hash, sp, z := s.Flag('+'), s.Flag('-'), s.Flag('#'), s.Flag(' '), s.Flag('0')
w, wp := s.Width()
p, pp := s.Precision()
if !plus && !minus && !hash && !sp && !z && !wp && !pp {
switch verb {
case 'v':
return true, "%v"
case 's':
return false, "%s"
case 'd':
return false, "%d"
}
// Other cases handled in the slow path below.
}
var f strings.Builder
f.WriteByte('%')
if plus {
f.WriteByte('+')
}
if minus {
f.WriteByte('-')
}
if hash {
f.WriteByte('#')
}
if sp {
f.WriteByte(' ')
}
if z {
f.WriteByte('0')
}
if wp {
f.WriteString(strconv.Itoa(w))
}
if pp {
f.WriteByte('.')
f.WriteString(strconv.Itoa(p))
}
f.WriteRune(verb)
return false, f.String()
}

View File

@@ -0,0 +1,40 @@
// 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)
StartLen = len(StartS)
End = '›'
EndS = string(End)
EndLen = len(EndS)
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

@@ -0,0 +1,99 @@
// 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 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
// 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 string
// StripMarkers removes the redaction markers from the
// 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), "")
}
// 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))
}
// ToBytes converts the string to a byte slice.
func (s RedactableString) ToBytes() RedactableBytes {
return RedactableBytes([]byte(string(s)))
}
// SafeFormat formats the redactable safely.
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)
}
// 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 []byte
// StripMarkers removes the redaction markers from the
// 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)
}
// Redact replaces all occurrences of unsafe substrings by the
// “Redacted” marker, ‹×›.
func (s RedactableBytes) Redact() RedactableBytes {
return RedactableBytes(ReStripSensitive.ReplaceAll(s, RedactedBytes))
}
// ToString converts the byte slice to a string.
func (s RedactableBytes) ToString() RedactableString {
return RedactableString(string([]byte(s)))
}
// SafeFormat formats the redactable safely.
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)
}
// StartMarker returns the start delimiter for an unsafe string.
func StartMarker() []byte { return []byte(StartS) }
// EndMarker returns the end delimiter for an unsafe string.
func EndMarker() []byte { return []byte(EndS) }
// RedactedMarker returns the special string used by Redact.
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, EscapeMarkBytes)
}

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

@@ -0,0 +1,84 @@
// 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 (
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.
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) 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
// 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{}) 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 _ 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 origFmt.State, verb rune) {
fmtforward.ReproducePrintf(s, s, verb, w.a)
}
// SafeMessage implements SafeMessager.
func (w safeWrapper) SafeMessage() string {
return origFmt.Sprintf("%v", w.a)
}

View File

@@ -0,0 +1,43 @@
Overall code structure
======================
This directory imports the "printf" logic as-is from the Go standard
library and instruments it for redaction of sensitive data.
Overall, the original Go code is structured as follows:
- the top-level API functions (`Printf` `Fprintf` etc) instantiate a
"printer" struct called `pp`, then call the `doPrint*()` methods
on it.
- the `pp` contains a byte slice (`pp.buf`, type `buffer`) that
accumulates the result of formatting *regardless of the final output
of the API* - i.e. a buffer is used even when using `Fprint` to an
`io.Writer`. Only after the `doPrint()` method finishes, is the
`buffer` copied to the final `io.Writer` in the `Fprint*` variants.
- each of the `doPrint` methods does some analysis on the argument
list - quite simple for e.g. `doPrintln`, more intricate for
`doPrintf`. As part of the analysis it emits "spacing" or no-op
bytes directly on the `pp.buf`. For example `doPrint` emits spaces
between arguments directly, `doPrintf` emits the non-formatting
characters from the format string, as well as certain *constant*
error strings (e.g. `%(BADPREC)`).
Refreshing the sources
======================
The files in this directory have been imported from
`$GOROOT/src/fmt/{format,print}.go`
and
`$GOROOT/src/internal/fmtsort`
And patched using the included `.diff` files.
To upgrade to a newer Go implementation, import the files anew and
re-apply the patches.
See the script `refresh.sh` for details.

View File

@@ -0,0 +1,222 @@
// Code generated from the Go standard library. DO NOT EDIT
// GENERATED FILE DO NOT EDIT
// Copyright 2018 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 fmtsort provides a general stable ordering mechanism
// for maps, on behalf of the fmt and text/template packages.
// It is not guaranteed to be efficient and works only for types
// that are valid map keys.
package fmtsort
import (
"reflect"
"sort"
)
// Note: Throughout this package we avoid calling reflect.Value.Interface as
// it is not always legal to do so and it's easier to avoid the issue than to face it.
// SortedMap represents a map's keys and values. The keys and values are
// aligned in index order: Value[i] is the value in the map corresponding to Key[i].
type SortedMap struct {
Key []reflect.Value
Value []reflect.Value
}
func (o *SortedMap) Len() int { return len(o.Key) }
func (o *SortedMap) Less(i, j int) bool { return compare(o.Key[i], o.Key[j]) < 0 }
func (o *SortedMap) Swap(i, j int) {
o.Key[i], o.Key[j] = o.Key[j], o.Key[i]
o.Value[i], o.Value[j] = o.Value[j], o.Value[i]
}
// Sort accepts a map and returns a SortedMap that has the same keys and
// values but in a stable sorted order according to the keys, modulo issues
// raised by unorderable key values such as NaNs.
//
// The ordering rules are more general than with Go's < operator:
//
// - when applicable, nil compares low
// - ints, floats, and strings order by <
// - NaN compares less than non-NaN floats
// - bool compares false before true
// - complex compares real, then imag
// - pointers compare by machine address
// - channel values compare by machine address
// - structs compare each field in turn
// - arrays compare each element in turn.
// Otherwise identical arrays compare by length.
// - interface values compare first by reflect.Type describing the concrete type
// and then by concrete value as described in the previous rules.
//
func Sort(mapValue reflect.Value) *SortedMap {
if mapValue.Type().Kind() != reflect.Map {
return nil
}
// Note: this code is arranged to not panic even in the presence
// of a concurrent map update. The runtime is responsible for
// yelling loudly if that happens. See issue 33275.
n := mapValue.Len()
key := make([]reflect.Value, 0, n)
value := make([]reflect.Value, 0, n)
iter := mapValue.MapRange()
for iter.Next() {
key = append(key, iter.Key())
value = append(value, iter.Value())
}
sorted := &SortedMap{
Key: key,
Value: value,
}
sort.Stable(sorted)
return sorted
}
// compare compares two values of the same type. It returns -1, 0, 1
// according to whether a > b (1), a == b (0), or a < b (-1).
// If the types differ, it returns -1.
// See the comment on Sort for the comparison rules.
func compare(aVal, bVal reflect.Value) int {
aType, bType := aVal.Type(), bVal.Type()
if aType != bType {
return -1 // No good answer possible, but don't return 0: they're not equal.
}
switch aVal.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
a, b := aVal.Int(), bVal.Int()
switch {
case a < b:
return -1
case a > b:
return 1
default:
return 0
}
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
a, b := aVal.Uint(), bVal.Uint()
switch {
case a < b:
return -1
case a > b:
return 1
default:
return 0
}
case reflect.String:
a, b := aVal.String(), bVal.String()
switch {
case a < b:
return -1
case a > b:
return 1
default:
return 0
}
case reflect.Float32, reflect.Float64:
return floatCompare(aVal.Float(), bVal.Float())
case reflect.Complex64, reflect.Complex128:
a, b := aVal.Complex(), bVal.Complex()
if c := floatCompare(real(a), real(b)); c != 0 {
return c
}
return floatCompare(imag(a), imag(b))
case reflect.Bool:
a, b := aVal.Bool(), bVal.Bool()
switch {
case a == b:
return 0
case a:
return 1
default:
return -1
}
case reflect.Ptr, reflect.UnsafePointer:
a, b := aVal.Pointer(), bVal.Pointer()
switch {
case a < b:
return -1
case a > b:
return 1
default:
return 0
}
case reflect.Chan:
if c, ok := nilCompare(aVal, bVal); ok {
return c
}
ap, bp := aVal.Pointer(), bVal.Pointer()
switch {
case ap < bp:
return -1
case ap > bp:
return 1
default:
return 0
}
case reflect.Struct:
for i := 0; i < aVal.NumField(); i++ {
if c := compare(aVal.Field(i), bVal.Field(i)); c != 0 {
return c
}
}
return 0
case reflect.Array:
for i := 0; i < aVal.Len(); i++ {
if c := compare(aVal.Index(i), bVal.Index(i)); c != 0 {
return c
}
}
return 0
case reflect.Interface:
if c, ok := nilCompare(aVal, bVal); ok {
return c
}
c := compare(reflect.ValueOf(aVal.Elem().Type()), reflect.ValueOf(bVal.Elem().Type()))
if c != 0 {
return c
}
return compare(aVal.Elem(), bVal.Elem())
default:
// Certain types cannot appear as keys (maps, funcs, slices), but be explicit.
panic("bad type in compare: " + aType.String())
}
}
// nilCompare checks whether either value is nil. If not, the boolean is false.
// If either value is nil, the boolean is true and the integer is the comparison
// value. The comparison is defined to be 0 if both are nil, otherwise the one
// nil value compares low. Both arguments must represent a chan, func,
// interface, map, pointer, or slice.
func nilCompare(aVal, bVal reflect.Value) (int, bool) {
if aVal.IsNil() {
if bVal.IsNil() {
return 0, true
}
return -1, true
}
if bVal.IsNil() {
return 1, true
}
return 0, false
}
// floatCompare compares two floating-point values. NaNs compare low.
func floatCompare(a, b float64) int {
switch {
case isNaN(a):
return -1 // No good answer if b is a NaN so don't bother checking.
case isNaN(b):
return 1
case a < b:
return -1
case a > b:
return 1
}
return 0
}
func isNaN(a float64) bool {
return a != a
}

View File

@@ -0,0 +1,589 @@
// 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 rfmt
import (
"strconv"
"unicode/utf8"
)
const (
ldigits = "0123456789abcdefx"
udigits = "0123456789ABCDEFX"
)
const (
signed = true
unsigned = false
)
// flags placed in a separate struct for easy clearing.
type fmtFlags struct {
widPresent bool
precPresent bool
minus bool
plus bool
sharp bool
space bool
zero bool
// For the formats %+v %#v, we set the plusV/sharpV flags
// and clear the plus/sharp flags since %+v and %#v are in effect
// different, flagless formats set at the top level.
plusV bool
sharpV bool
}
// A fmt is the raw formatter used by Printf etc.
// It prints into a buffer that must be set up separately.
type fmt struct {
buf *buffer
fmtFlags
wid int // width
prec int // precision
// intbuf is large enough to store %b of an int64 with a sign and
// avoids padding at the end of the struct on 32 bit architectures.
intbuf [68]byte
}
func (f *fmt) clearflags() {
f.fmtFlags = fmtFlags{}
}
func (f *fmt) init(buf *buffer) {
f.buf = buf
f.clearflags()
}
// writePadding generates n bytes of padding.
func (f *fmt) writePadding(n int) {
if n <= 0 { // No padding bytes needed.
return
}
// Make enough room for padding.
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.
for i := 0; i < n; i++ {
f.buf.writeByte(padByte)
}
}
// pad appends b to f.buf, padded on left (!f.minus) or right (f.minus).
func (f *fmt) pad(b []byte) {
if !f.widPresent || f.wid == 0 {
f.buf.write(b)
return
}
width := f.wid - utf8.RuneCount(b)
if !f.minus {
// left padding
f.writePadding(width)
f.buf.write(b)
} else {
// right padding
f.buf.write(b)
f.writePadding(width)
}
}
// padString appends s to f.buf, padded on left (!f.minus) or right (f.minus).
func (f *fmt) padString(s string) {
if !f.widPresent || f.wid == 0 {
f.buf.writeString(s)
return
}
width := f.wid - utf8.RuneCountInString(s)
if !f.minus {
// left padding
f.writePadding(width)
f.buf.writeString(s)
} else {
// right padding
f.buf.writeString(s)
f.writePadding(width)
}
}
// fmtBoolean formats a boolean.
func (f *fmt) fmtBoolean(v bool) {
if v {
f.padString("true")
} else {
f.padString("false")
}
}
// fmtUnicode formats a uint64 as "U+0078" or with f.sharp set as "U+0078 'x'".
func (f *fmt) fmtUnicode(u uint64) {
buf := f.intbuf[0:]
// With default precision set the maximum needed buf length is 18
// for formatting -1 with %#U ("U+FFFFFFFFFFFFFFFF") which fits
// into the already allocated intbuf with a capacity of 68 bytes.
prec := 4
if f.precPresent && f.prec > 4 {
prec = f.prec
// Compute space needed for "U+" , number, " '", character, "'".
width := 2 + prec + 2 + utf8.UTFMax + 1
if width > len(buf) {
buf = make([]byte, width)
}
}
// Format into buf, ending at buf[i]. Formatting numbers is easier right-to-left.
i := len(buf)
// For %#U we want to add a space and a quoted character at the end of the buffer.
if f.sharp && u <= utf8.MaxRune && strconv.IsPrint(rune(u)) {
i--
buf[i] = '\''
i -= utf8.RuneLen(rune(u))
utf8.EncodeRune(buf[i:], rune(u))
i--
buf[i] = '\''
i--
buf[i] = ' '
}
// Format the Unicode code point u as a hexadecimal number.
for u >= 16 {
i--
buf[i] = udigits[u&0xF]
prec--
u >>= 4
}
i--
buf[i] = udigits[u]
prec--
// Add zeros in front of the number until requested precision is reached.
for prec > 0 {
i--
buf[i] = '0'
prec--
}
// Add a leading "U+".
i--
buf[i] = '+'
i--
buf[i] = 'U'
oldZero := f.zero
f.zero = false
f.pad(buf[i:])
f.zero = oldZero
}
// fmtInteger formats signed and unsigned integers.
func (f *fmt) fmtInteger(u uint64, base int, isSigned bool, verb rune, digits string) {
negative := isSigned && int64(u) < 0
if negative {
u = -u
}
buf := f.intbuf[0:]
// The already allocated f.intbuf with a capacity of 68 bytes
// is large enough for integer formatting when no precision or width is set.
if f.widPresent || f.precPresent {
// Account 3 extra bytes for possible addition of a sign and "0x".
width := 3 + f.wid + f.prec // wid and prec are always positive.
if width > len(buf) {
// We're going to need a bigger boat.
buf = make([]byte, width)
}
}
// Two ways to ask for extra leading zero digits: %.3d or %03d.
// If both are specified the f.zero flag is ignored and
// padding with spaces is used instead.
prec := 0
if f.precPresent {
prec = f.prec
// Precision of 0 and value of 0 means "print nothing" but padding.
if prec == 0 && u == 0 {
oldZero := f.zero
f.zero = false
f.writePadding(f.wid)
f.zero = oldZero
return
}
} else if f.zero && f.widPresent {
prec = f.wid
if negative || f.plus || f.space {
prec-- // leave room for sign
}
}
// Because printing is easier right-to-left: format u into buf, ending at buf[i].
// We could make things marginally faster by splitting the 32-bit case out
// into a separate block but it's not worth the duplication, so u has 64 bits.
i := len(buf)
// Use constants for the division and modulo for more efficient code.
// Switch cases ordered by popularity.
switch base {
case 10:
for u >= 10 {
i--
next := u / 10
buf[i] = byte('0' + u - next*10)
u = next
}
case 16:
for u >= 16 {
i--
buf[i] = digits[u&0xF]
u >>= 4
}
case 8:
for u >= 8 {
i--
buf[i] = byte('0' + u&7)
u >>= 3
}
case 2:
for u >= 2 {
i--
buf[i] = byte('0' + u&1)
u >>= 1
}
default:
panic("fmt: unknown base; can't happen")
}
i--
buf[i] = digits[u]
for i > 0 && prec > len(buf)-i {
i--
buf[i] = '0'
}
// Various prefixes: 0x, -, etc.
if f.sharp {
switch base {
case 2:
// Add a leading 0b.
i--
buf[i] = 'b'
i--
buf[i] = '0'
case 8:
if buf[i] != '0' {
i--
buf[i] = '0'
}
case 16:
// Add a leading 0x or 0X.
i--
buf[i] = digits[16]
i--
buf[i] = '0'
}
}
if verb == 'O' {
i--
buf[i] = 'o'
i--
buf[i] = '0'
}
if negative {
i--
buf[i] = '-'
} else if f.plus {
i--
buf[i] = '+'
} else if f.space {
i--
buf[i] = ' '
}
// Left padding with zeros has already been handled like precision earlier
// or the f.zero flag is ignored due to an explicitly set precision.
oldZero := f.zero
f.zero = false
f.pad(buf[i:])
f.zero = oldZero
}
// truncateString truncates the string s to the specified precision, if present.
func (f *fmt) truncateString(s string) string {
if f.precPresent {
n := f.prec
for i := range s {
n--
if n < 0 {
return s[:i]
}
}
}
return s
}
// truncate truncates the byte slice b as a string of the specified precision, if present.
func (f *fmt) truncate(b []byte) []byte {
if f.precPresent {
n := f.prec
for i := 0; i < len(b); {
n--
if n < 0 {
return b[:i]
}
wid := 1
if b[i] >= utf8.RuneSelf {
_, wid = utf8.DecodeRune(b[i:])
}
i += wid
}
}
return b
}
// fmtS formats a string.
func (f *fmt) fmtS(s string) {
s = f.truncateString(s)
f.padString(s)
}
// fmtBs formats the byte slice b as if it was formatted as string with fmtS.
func (f *fmt) fmtBs(b []byte) {
b = f.truncate(b)
f.pad(b)
}
// fmtSbx formats a string or byte slice as a hexadecimal encoding of its bytes.
func (f *fmt) fmtSbx(s string, b []byte, digits string) {
length := len(b)
if b == nil {
// No byte slice present. Assume string s should be encoded.
length = len(s)
}
// Set length to not process more bytes than the precision demands.
if f.precPresent && f.prec < length {
length = f.prec
}
// Compute width of the encoding taking into account the f.sharp and f.space flag.
width := 2 * length
if width > 0 {
if f.space {
// Each element encoded by two hexadecimals will get a leading 0x or 0X.
if f.sharp {
width *= 2
}
// Elements will be separated by a space.
width += length - 1
} else if f.sharp {
// Only a leading 0x or 0X will be added for the whole string.
width += 2
}
} else { // The byte slice or string that should be encoded is empty.
if f.widPresent {
f.writePadding(f.wid)
}
return
}
// Handle padding to the left.
if f.widPresent && f.wid > width && !f.minus {
f.writePadding(f.wid - width)
}
// Write the encoding directly into the output buffer.
if f.sharp {
// Add leading 0x or 0X.
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.
f.buf.WriteByte(' ')
if f.sharp {
// Add leading 0x or 0X for each element.
f.buf.WriteByte('0')
f.buf.WriteByte(digits[16])
}
}
if b != nil {
c = b[i] // Take a byte from the input byte slice.
} else {
c = s[i] // Take a byte from the input string.
}
// Encode each byte as two hexadecimal digits.
f.buf.WriteByte(digits[c>>4])
f.buf.WriteByte(digits[c&0xF])
}
// Handle padding to the right.
if f.widPresent && f.wid > width && f.minus {
f.writePadding(f.wid - width)
}
}
// fmtSx formats a string as a hexadecimal encoding of its bytes.
func (f *fmt) fmtSx(s, digits string) {
f.fmtSbx(s, nil, digits)
}
// fmtBx formats a byte slice as a hexadecimal encoding of its bytes.
func (f *fmt) fmtBx(b []byte, digits string) {
f.fmtSbx("", b, digits)
}
// fmtQ formats a string as a double-quoted, escaped Go string constant.
// If f.sharp is set a raw (backquoted) string may be returned instead
// if the string does not contain any control characters other than tab.
func (f *fmt) fmtQ(s string) {
s = f.truncateString(s)
if f.sharp && strconv.CanBackquote(s) {
f.padString("`" + s + "`")
return
}
buf := f.intbuf[:0]
if f.plus {
f.pad(strconv.AppendQuoteToASCII(buf, s))
} else {
f.pad(strconv.AppendQuote(buf, s))
}
}
// fmtC formats an integer as a Unicode character.
// If the character is not valid Unicode, it will print '\ufffd'.
func (f *fmt) fmtC(c uint64) {
r := rune(c)
if c > utf8.MaxRune {
r = utf8.RuneError
}
buf := f.intbuf[:0]
w := utf8.EncodeRune(buf[:utf8.UTFMax], r)
f.pad(buf[:w])
}
// fmtQc formats an integer as a single-quoted, escaped Go character constant.
// If the character is not valid Unicode, it will print '\ufffd'.
func (f *fmt) fmtQc(c uint64) {
r := rune(c)
if c > utf8.MaxRune {
r = utf8.RuneError
}
buf := f.intbuf[:0]
if f.plus {
f.pad(strconv.AppendQuoteRuneToASCII(buf, r))
} else {
f.pad(strconv.AppendQuoteRune(buf, r))
}
}
// fmtFloat formats a float64. It assumes that verb is a valid format specifier
// for strconv.AppendFloat and therefore fits into a byte.
func (f *fmt) fmtFloat(v float64, size int, verb rune, prec int) {
// Explicit precision in format specifier overrules default precision.
if f.precPresent {
prec = f.prec
}
// Format number, reserving space for leading + sign if needed.
num := strconv.AppendFloat(f.intbuf[:1], v, byte(verb), prec, size)
if num[1] == '-' || num[1] == '+' {
num = num[1:]
} else {
num[0] = '+'
}
// f.space means to add a leading space instead of a "+" sign unless
// the sign is explicitly asked for by f.plus.
if f.space && num[0] == '+' && !f.plus {
num[0] = ' '
}
// Special handling for infinities and NaN,
// which don't look like a number so shouldn't be padded with zeros.
if num[1] == 'I' || num[1] == 'N' {
oldZero := f.zero
f.zero = false
// Remove sign before NaN if not asked for.
if num[1] == 'N' && !f.space && !f.plus {
num = num[1:]
}
f.pad(num)
f.zero = oldZero
return
}
// The sharp flag forces printing a decimal point for non-binary formats
// and retains trailing zeros, which we may need to restore.
if f.sharp && verb != 'b' {
digits := 0
switch verb {
case 'v', 'g', 'G', 'x':
digits = prec
// If no precision is set explicitly use a precision of 6.
if digits == -1 {
digits = 6
}
}
// Buffer pre-allocated with enough room for
// exponent notations of the form "e+123" or "p-1023".
var tailBuf [6]byte
tail := tailBuf[:0]
hasDecimalPoint := false
sawNonzeroDigit := false
// Starting from i = 1 to skip sign at num[0].
for i := 1; i < len(num); i++ {
switch num[i] {
case '.':
hasDecimalPoint = true
case 'p', 'P':
tail = append(tail, num[i:]...)
num = num[:i]
case 'e', 'E':
if verb != 'x' && verb != 'X' {
tail = append(tail, num[i:]...)
num = num[:i]
break
}
fallthrough
default:
if num[i] != '0' {
sawNonzeroDigit = true
}
// Count significant digits after the first non-zero digit.
if sawNonzeroDigit {
digits--
}
}
}
if !hasDecimalPoint {
// Leading digit 0 should contribute once to digits.
if len(num) == 2 && num[1] == '0' {
digits--
}
num = append(num, '.')
}
for digits > 0 {
num = append(num, '0')
digits--
}
num = append(num, tail...)
}
// We want a sign if asked for and if the sign is not positive.
if f.plus || num[0] != '+' {
// If we're zero padding to the left we want the sign before the leading zeros.
// Achieve this by writing the sign out and then padding the unsigned number.
if f.zero && f.widPresent && f.wid > len(num) {
f.buf.writeByte(num[0])
f.writePadding(f.wid - len(num))
f.buf.write(num[1:])
return
}
f.pad(num)
return
}
// No sign to show and the number is positive; just print the unsigned number.
f.pad(num[1:])
}

View File

@@ -0,0 +1,81 @@
--- format.go.orig 2022-01-07 15:25:57.762807000 +0100
+++ format.go 2022-01-07 15:26:16.154491000 +0100
@@ -1,8 +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"
@@ -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{})
)

File diff suppressed because it is too large Load Diff

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,97 @@
// 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))
}
// SafeByte implements SafePrinter.
func (p *pp) SafeByte(r i.SafeByte) {
defer p.startSafeOverride().restore()
p.buf.WriteByte(byte(r))
}
// SafeBytes implements SafePrinter.
func (p *pp) SafeBytes(r i.SafeBytes) {
defer p.startSafeOverride().restore()
p.buf.Write(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

@@ -0,0 +1,26 @@
#!/usr/bin/env bash
#
# This file re-generates the sources in this directory from the Go
# standard library.
#
set -euxo pipefail
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
mkdir -p fmtsort
for a in $GOROOT/src/internal/fmtsort/*; do
if expr "$a" : ".*_test.go"; then
continue
fi
n=$(basename "$a")
(
echo "// Code generated from the Go standard library. DO NOT EDIT"
echo "// GENERATED FILE DO NOT EDIT"
cat "$a"
) >fmtsort/"$n"
done

View File

@@ -0,0 +1,46 @@
// 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 rfmt
import (
"reflect"
i "github.com/cockroachdb/redact/interfaces"
)
// RegisterSafeType registers a data type to always be considered safe
// during the production of redactable strings.
func RegisterSafeType(t reflect.Type) {
safeTypeRegistry[t] = true
}
// safeTypeRegistry registers Go data types that are to be always
// considered safe, even when they don't implement SafeValue.
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
}

87
vendor/github.com/cockroachdb/redact/markers_print.go generated vendored Normal file
View File

@@ -0,0 +1,87 @@
// 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 (
"io"
"github.com/cockroachdb/redact/internal/rfmt"
)
// Sprint prints out the arguments and encloses unsafe bits
// between redaction markers.
// If either safe and unsafe bits of data contain the markers
// in their representation already, they are escaped first.
// If a RedactableString or RedactableBytes argument is passed,
// it is reproduced as-is without escaping.
func Sprint(args ...interface{}) RedactableString {
return rfmt.Sprint(args...)
}
// Sprintf formats the arguments and encloses unsafe bits
// between redaction markers.
// If either safe and unsafe bits of data contain the markers
// in their representation already, they are escaped first.
// The format is always considered safe and the caller
// is responsible to ensure that the markers are not present
// in the format string.
func Sprintf(format string, args ...interface{}) RedactableString {
return rfmt.Sprintf(format, args...)
}
// 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{}) (RedactableString, error) {
return rfmt.HelperForErrorf(format, args...)
}
// Sprintfn produces a RedactableString using the provided
// SafeFormat-alike function.
func Sprintfn(printer func(w SafePrinter)) RedactableString {
return rfmt.Sprintfn(printer)
}
// StringWithoutMarkers formats the provided SafeFormatter and strips
// the redaction markers from the result. This is provided for
// convenience to facilitate the implementation of String() methods
// alongside SafeFormat() to avoid code duplication.
//
// Note: if this function is ever found to be a performance
// bottleneck, one can consider using an alternate implementation of
// Sprint() which similarly calls the SafeFormat() methods but does
// not introduce markers and instead writes to a string buffer
// directly.
func StringWithoutMarkers(f SafeFormatter) string {
return Sprint(f).StripMarkers()
}
// Fprint is like Sprint but outputs the redactable
// string to the provided Writer.
func Fprint(w io.Writer, args ...interface{}) (n int, err error) {
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) {
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] }