Files
kjol/tools/tsgo/internal/lsp/stack_sanitizer.go
2026-07-09 16:50:43 -04:00

97 lines
2.6 KiB
Go

package lsp
import (
"regexp"
"strings"
"github.com/microsoft/typescript-go/internal/core"
)
// VS Code's telemetry pipeline redacts any string matching
// /(key|token|sig|secret|signature|password|passwd|pwd|android:value)[^a-zA-Z0-9]/i
// as `<REDACTED: Generic Secret>`, which trips on innocuous Go frames like
// `getSignatureHelp(`. Insert `X_X` after each trigger keyword that we know
// can appear in our sanitized output, when followed by punctuation we
// actually emit (`(`, `[`, `.`, `|`); reverse by removing the marker (replace
// `X_X` with the empty string) on the dashboard.
var genericSecretKeywordRegex = regexp.MustCompile(`(?i)(key|token|signature|sig|pwd)([(\[.|])`)
func defeatGenericSecretRegex(s string) string {
return genericSecretKeywordRegex.ReplaceAllString(s, "${1}X_X${2}")
}
func sanitizeStackTrace(stack string) string {
// TODO: should we just look for the first '(' and
// just strip everything before the prior newline?
startIndex := strings.Index(stack, "runtime/debug.Stack()")
if startIndex < 0 {
return ""
}
stack = stack[startIndex:]
result := &strings.Builder{}
for lineNum, line := range core.Enumerate(strings.Lines(stack)) {
if lineNum > 0 {
result.WriteByte('\n')
}
i := 0
// Skip whitespace
for i < len(line) {
if line[i] != ' ' && line[i] != '\t' {
break
}
i++
}
result.WriteString(line[:i])
line = line[i:]
ourModuleIndex := strings.Index(line, "typescript-go/internal")
if ourModuleIndex >= 0 {
line = line[ourModuleIndex:]
writeSanitizedModuleOrPath(line, result)
} else {
result.WriteString("(REDACTED FRAME)")
}
}
return defeatGenericSecretRegex(result.String())
}
func writeSanitizedModuleOrPath(line string, result *strings.Builder) {
// We don't expect things like \r, but it doesn't hurt to trim just in case.
line = strings.TrimSpace(line)
if plusHex := strings.Index(line, " +0x"); plusHex >= 0 {
line = line[:plusHex]
} else if inGoroutine := strings.LastIndex(line, " in goroutine "); inGoroutine >= 0 {
line = line[:inGoroutine]
}
for segmentIndex, segment := range strings.Split(line, "/") {
if segmentIndex > 0 {
result.WriteString("|>")
}
// See if the string ends with ), and strip out all the arguments.
if strings.HasSuffix(segment, ")") {
openParenIndex := strings.LastIndexByte(segment, '(')
if openParenIndex < 0 {
// Closing parenthesis, but no opening - bail out.
result.WriteString("???")
continue
}
segment = segment[:openParenIndex]
result.WriteString(segment)
result.WriteString("()")
continue
}
result.WriteString(segment)
}
}