Skip to main content
Glama
obfuscate.go4.37 kB
package obfuscation import ( "fmt" "strconv" "strings" "golang.org/x/exp/slices" "github.com/rs/zerolog/log" "github.com/valyala/fastjson" ) type Obfuscator struct { Hasher Hasher } func (obfuscator Obfuscator) ObfuscateString(raw string) string { return obfuscator.Hasher.HashBytes([]byte(raw)) } var ( parserPool fastjson.ParserPool arenaPool fastjson.ArenaPool ) func (obfuscator Obfuscator) ObfuscateJSON( raw string, excludedPaths []string, ) (string, error) { parser := parserPool.Get() defer parserPool.Put(parser) json, err := parser.Parse(raw) if err != nil { return "", err } arena := arenaPool.Get() defer arenaPool.Put(arena) obfuscatedJSON, err := obfuscator.obfuscateJSON( *json, arena, "", excludedPaths, false, ) if err != nil { return "", err } bytes := obfuscatedJSON.MarshalTo([]byte{}) return string(bytes), nil } func (obfuscator Obfuscator) obfuscateJSON( raw fastjson.Value, arena *fastjson.Arena, cursor string, excludedPaths []string, onExcludedPath bool, ) (*fastjson.Value, error) { var obfuscatedJSON *fastjson.Value onExcludedPath = onExcludedPath || isCursorInExcludedPath(cursor, excludedPaths) log.Trace(). Msgf("cursor: %v, onExcludedPath: %v", cursor, onExcludedPath) if onExcludedPath { return &raw, nil } switch raw.Type() { // Handle complex JSON types which require recursive handling case fastjson.TypeArray: array, err := raw.Array() if err != nil { return nil, err } obfuscatedArray := arena.NewArray() for i, item := range array { //nolint:varnamelen obfuscatedItem, err := obfuscator.obfuscateJSON( *item, arena, fmt.Sprintf("%s[]", cursor), excludedPaths, onExcludedPath, ) if err != nil { return nil, err } obfuscatedArray.SetArrayItem(i, obfuscatedItem) } obfuscatedJSON = obfuscatedArray case fastjson.TypeObject: object, err := raw.Object() if err != nil { return nil, err } keys := getKeys(object) obfuscatedObject := arena.NewObject() for _, key := range keys { value := object.Get(key) obfuscatedValue, err := obfuscator.obfuscateJSON( *value, arena, fmt.Sprintf("%s.%s", cursor, key), excludedPaths, onExcludedPath, ) if err != nil { return nil, err } obfuscatedObject.Set(key, obfuscatedValue) } obfuscatedJSON = obfuscatedObject // Handle primitive JSON types case fastjson.TypeNumber: number, err := raw.Float64() if err != nil { return nil, err } // we are keeping two decimal points for all numbers before obfuscation - // 10 will become 10.00, 10.999 will become 10.99 str := strconv.FormatFloat(number, 'f', 2, 64) obfuscatedString := arena.NewString( obfuscator.Hasher.HashBytes([]byte(str)), ) obfuscatedJSON = obfuscatedString case fastjson.TypeString: stringBytes, err := raw.StringBytes() if err != nil { return nil, err } obfuscatedString := arena.NewString( obfuscator.Hasher.HashBytes(stringBytes), ) obfuscatedJSON = obfuscatedString // Handle constant JSON types case fastjson.TypeTrue: obfuscatedString := arena.NewString( obfuscator.Hasher.HashBytes([]byte("true")), ) obfuscatedJSON = obfuscatedString case fastjson.TypeFalse: obfuscatedString := arena.NewString( obfuscator.Hasher.HashBytes([]byte("false")), ) obfuscatedJSON = obfuscatedString case fastjson.TypeNull: obfuscatedString := arena.NewString( obfuscator.Hasher.HashBytes([]byte("null")), ) obfuscatedJSON = obfuscatedString } return obfuscatedJSON, nil } // isCursorInExcludedPath checks if the given path segment should be excluded from obfuscation // usage only slices.Contains(excludedPaths, cursor) cannot work for JSONPath exclusions, // since it compares the whole string and works only for simple strings exclusions func isCursorInExcludedPath(cursor string, excludedPaths []string) bool { // simple string comparison if slices.Contains(excludedPaths, cursor) { return true } // json path support if cursor == "" { return false } for _, path := range excludedPaths { if strings.HasSuffix(path, cursor) { return true } } return false } func getKeys(object *fastjson.Object) []string { keys := []string{} collectKeys := func(key []byte, _ *fastjson.Value) { keys = append(keys, string(key)) } object.Visit(collectKeys) return keys }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/TheLunarCompany/lunar'

If you have feedback or need assistance with the MCP directory API, please join our Discord server