|
6 | 6 | package cli
|
7 | 7 |
|
8 | 8 | import (
|
| 9 | + "bufio" |
9 | 10 | "bytes"
|
10 | 11 | "compress/gzip"
|
11 | 12 | "context"
|
@@ -37,6 +38,7 @@ import (
|
37 | 38 | "github.com/cockroachdb/cockroach/pkg/util/system"
|
38 | 39 | "github.com/cockroachdb/cockroach/pkg/util/timeutil"
|
39 | 40 | "github.com/cockroachdb/errors"
|
| 41 | + "github.com/cockroachdb/errors/oserror" |
40 | 42 | "github.com/spf13/cobra"
|
41 | 43 | "golang.org/x/oauth2/google"
|
42 | 44 | "google.golang.org/api/option"
|
@@ -202,13 +204,114 @@ func uploadJSONFile(fileName string, message any, uuid string) error {
|
202 | 204 | // pipeline which enriches the logs with more fields.
|
203 | 205 | var defaultDDTags = []string{"service:CRDB-SH", "env:debug", "source:cockroachdb"}
|
204 | 206 |
|
| 207 | +// buildRedactionWarning creates a warning message about sensitive data in debug zips. |
| 208 | +// It includes a common list of sensitive data types that may be present. |
| 209 | +func buildRedactionWarning(prefix string) string { |
| 210 | + return prefix + |
| 211 | + "This means it may contain sensitive data including:\n" + |
| 212 | + " • Personally Identifiable Information (PII)\n" + |
| 213 | + " • Database credentials and connection strings\n" + |
| 214 | + " • Internal cluster details\n" + |
| 215 | + " • Potentially sensitive log data\n\n" + |
| 216 | + "It is advisable to only upload redacted debug zips to Datadog.\n" |
| 217 | +} |
| 218 | + |
| 219 | +// promptUserForConfirmationImpl shows a warning message and prompts the user for confirmation. |
| 220 | +// It returns nil if the user confirms, or an error if they decline or if there's an input error. |
| 221 | +// In dry-run mode, it shows the warning but skips the prompt. |
| 222 | +func promptUserForConfirmationImpl(warningMsg string) error { |
| 223 | + // Skip interactive prompt in dry-run mode |
| 224 | + if debugZipUploadOpts.dryRun { |
| 225 | + fmt.Fprintf(os.Stderr, "%s", warningMsg) |
| 226 | + fmt.Fprintf(os.Stderr, "DRY RUN: Would prompt for confirmation here.\n") |
| 227 | + return nil |
| 228 | + } |
| 229 | + |
| 230 | + fmt.Fprintf(os.Stderr, "%s", warningMsg) |
| 231 | + fmt.Fprintf(os.Stderr, "Do you want to continue with the upload? (y/N): ") |
| 232 | + |
| 233 | + reader := bufio.NewReader(os.Stdin) |
| 234 | + line, err := reader.ReadString('\n') |
| 235 | + if err != nil { |
| 236 | + return fmt.Errorf("failed to read user input: %w", err) |
| 237 | + } |
| 238 | + |
| 239 | + line = strings.ToLower(strings.TrimSpace(line)) |
| 240 | + if len(line) == 0 { |
| 241 | + line = "n" // Default to 'no' when user presses enter without input |
| 242 | + } |
| 243 | + |
| 244 | + if line == "y" || line == "yes" { |
| 245 | + fmt.Fprintf(os.Stderr, "Proceeding with upload...\n") |
| 246 | + return nil |
| 247 | + } |
| 248 | + |
| 249 | + return fmt.Errorf("upload aborted") |
| 250 | +} |
| 251 | + |
| 252 | +// promptUserForConfirmation is a variable that can be mocked in tests |
| 253 | +var promptUserForConfirmation = promptUserForConfirmationImpl |
| 254 | + |
| 255 | +// validateRedactionStatus checks if the debug zip was created with redaction enabled |
| 256 | +// by examining the debugZipCommandFlagsFileName file in the system tenant. |
| 257 | +// If redaction is not enabled, it warns the user and prompts for confirmation. |
| 258 | +// |
| 259 | +// User experience examples: |
| 260 | +// |
| 261 | +// 1. Redacted zip (--redact=true): Proceeds silently |
| 262 | +// 2. Unredacted zip (--redact=false): Shows warning and prompts: |
| 263 | +// "⚠️ WARNING: Your debug zip was created WITHOUT redaction..." |
| 264 | +// "Do you want to continue with the upload? (y/N): " |
| 265 | +// 3. Unknown redaction status: Shows warning and prompts similarly |
| 266 | +// |
| 267 | +// The function respects dry-run mode by showing warnings without prompting. |
| 268 | +func validateRedactionStatus(debugDirPath string) error { |
| 269 | + flagsFilePath := path.Join(debugDirPath, debugZipCommandFlagsFileName) |
| 270 | + |
| 271 | + flagsContent, err := os.ReadFile(flagsFilePath) |
| 272 | + if err != nil { |
| 273 | + if oserror.IsNotExist(err) { |
| 274 | + // File doesn't exist - we can't determine redaction status, so warn and prompt |
| 275 | + warningMsg := buildRedactionWarning( |
| 276 | + "⚠️ WARNING: The debug zip redaction status is unclear.\n") |
| 277 | + |
| 278 | + return promptUserForConfirmation(warningMsg) |
| 279 | + } |
| 280 | + return fmt.Errorf("⚠️ error: the debug zip redaction status is unclear, err: %w", err) |
| 281 | + } |
| 282 | + |
| 283 | + flagsStr := string(flagsContent) |
| 284 | + |
| 285 | + if strings.Contains(flagsStr, "--redact=true") { |
| 286 | + return nil |
| 287 | + } |
| 288 | + |
| 289 | + var warningMsg string |
| 290 | + if strings.Contains(flagsStr, "--redact=false") { |
| 291 | + warningMsg = buildRedactionWarning( |
| 292 | + "⚠️ WARNING: The debug zip was created WITHOUT redaction.\n", |
| 293 | + ) |
| 294 | + } else { |
| 295 | + warningMsg = buildRedactionWarning( |
| 296 | + "⚠️ WARNING: The debug zip redaction status is unclear.\n", |
| 297 | + ) |
| 298 | + } |
| 299 | + |
| 300 | + return promptUserForConfirmation(warningMsg) |
| 301 | +} |
| 302 | + |
205 | 303 | func runDebugZipUpload(cmd *cobra.Command, args []string) error {
|
206 | 304 | runtime.GOMAXPROCS(system.NumCPU())
|
207 | 305 |
|
208 | 306 | if err := validateZipUploadReadiness(); err != nil {
|
209 | 307 | return err
|
210 | 308 | }
|
211 | 309 |
|
| 310 | + // Check redaction status before proceeding with upload |
| 311 | + if err := validateRedactionStatus(args[0]); err != nil { |
| 312 | + return err |
| 313 | + } |
| 314 | + |
212 | 315 | // a unique ID for this upload session. This should be used to tag all the artifacts uploaded in this session
|
213 | 316 | uploadID := newUploadID(debugZipUploadOpts.clusterName, getCurrentTime())
|
214 | 317 |
|
|
0 commit comments