Skip to content

Add MaskedString and other utilities - #91

Merged
jhaynie merged 4 commits into
mainfrom
masked-string
Sep 6, 2025
Merged

jhaynie merged 4 commits into
mainfrom
masked-string

Conversation

@jhaynie

@jhaynie jhaynie commented Sep 6, 2025 •

Copy link
Copy Markdown
Member

Summary by CodeRabbit

  • New Features
    • Secure masking improvements: consistent masking for URLs, emails, and tokens across formatting, logging, and display.
    • New masked value type that shows masked text to users while preserving raw values in JSON/YAML serialization.
    • Localhost detection expanded to handle full URLs, host:port, and IPv6 (e.g., ::1).
  • Bug Fixes
    • More reliable masking behavior for empty and Unicode values.
  • Chores
    • Dependency updates to support YAML handling.

@coderabbitai

coderabbitai Bot commented Sep 6, 2025 •

Copy link
Copy Markdown
Contributor

Walkthrough

Dependency update promotes yaml.v3 to a direct requirement. Introduces MaskValue and a new MaskedString type with marshaling semantics; refactors MaskArguments. Adds sys utilities: generic Ptr, Result[T], and enhanced IsLocalhost parsing/logic with tests. Minor string pointer refactor. Expands unit tests and logging test helper.

Changes

Cohort / File(s) Summary
Dependencies
go.mod
Move gopkg.in/yaml.v3 v3.0.1 from indirect to direct require.
String masking API
string/mask.go, string/mask_test.go
Add MaskValue and MaskedString type with String/Text/JSON/YAML/GoString behaviors; refactor MaskArguments to use MaskValue. Large new test suite covering formatting, logging, JSON/YAML round-trips.
String helpers
string/string.go
Use sys.Ptr in StringPointer and ClearEmptyStringPointer; behavior change: empty string yields non-nil pointer in StringPointer.
Networking: localhost detection
sys/net.go, sys/net_test.go
Expand IsLocalhost to accept URLs or host[:port]; robust host extraction, IPv4/IPv6 loopback/unspecified handling; add IPv6 tests.
Result utility
sys/result.go, sys/result_test.go
Add generic Result[T] with IsOk, IsErr, IsErrMatches, and constructors Ok, Err; comprehensive tests across types and error matching.
Pointer utility
sys/pointer.go
Add generic Ptr[T any](v T) *T with unit tests for multiple types and nil interface case.
Logger test helper
logger/console_test.go
Safer captureOutput: save/restore previous writer; trim captured output; add strings import.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant App
  participant MaskedString
  participant JSON as encoding/json
  participant YAML as gopkg.in/yaml.v3

  Note over App,MaskedString: Construct and use MaskedString
  App->>MaskedString: NewMaskedString("secret")
  App->>MaskedString: String()
  MaskedString-->>App: masked value

  App->>JSON: Marshal(MaskedString)
  JSON->>MaskedString: MarshalJSON()
  MaskedString-->>JSON: unmasked JSON string
  JSON-->>App: bytes

  App->>YAML: Marshal(MaskedString)
  YAML->>MaskedString: MarshalYAML()
  MaskedString-->>YAML: unmasked YAML scalar
  YAML-->>App: bytes
Loading
sequenceDiagram
  autonumber
  participant Caller
  participant Net as sys.IsLocalhost
  participant URL as net/url
  participant NetPkg as net

  Caller->>Net: IsLocalhost(input)
  alt parseable URL
    Net->>URL: Parse(input)
    URL-->>Net: URL
    Net->>URL: Hostname()
    URL-->>Net: host
  else host[:port] or literal
    Net->>NetPkg: SplitHostPort(input)?
    NetPkg-->>Net: host or error
    Note over Net: if IPv6 [::1] trim brackets
  end
  Net->>NetPkg: ParseIP(host)
  alt localhost/loopback/unspecified
    Net-->>Caller: true
  else
    Net-->>Caller: false
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~30 minutes

Suggested reviewers

  • robindiddams

Poem

A whisk of code, a hop of tests,
I mask my strings in clever vests.
URLs tamed, results in tow,
Localhost learns new lanes to know.
With Ptrs that point and logs that sing,
I thump my paw—ship this thing! 🐇✨

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch masked-string

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (7)
string/mask.go (3)

74-90: Flatten branching in MaskValue for readability.

Same behavior, less nesting.

Apply:

 func MaskValue(arg string) string {
-	if isURL.MatchString(arg) {
-		u, err := MaskURL(arg)
-		if err == nil {
-			return u
-		} else {
-			return Mask(arg)
-		}
-	} else if isEmail.MatchString(arg) {
-		return MaskEmail(arg)
-	} else if isJWT.MatchString(arg) {
-		return Mask(arg)
-	} else {
-		return arg
-	}
+	if isURL.MatchString(arg) {
+		if u, err := MaskURL(arg); err == nil {
+		 return u
+		}
+		return Mask(arg)
+	}
+	if isEmail.MatchString(arg) {
+		return MaskEmail(arg)
+	}
+	if isJWT.MatchString(arg) {
+		return Mask(arg)
+	}
+	return arg
 }

Optional: consider broadening isURL to RFC 3986 scheme regex ^[A-Za-z][A-Za-z0-9+.-]*:// to catch schemes with +, ., -.


101-140: Document semantics and ensure %#v prints masked.

Add a clearer doc comment and GoStringer so %#v is also redacted.

Apply:

-// MaskedString is a custom string type that masks its value when formatted or text-marshaled.
+// MaskedString holds a sensitive string.
+// Behavior:
+// - String()/fmt %s,%v: masked
+// - MarshalText: masked
+// - MarshalJSON/YAML: unmasked
+// - Text()/Bytes(): unmasked helpers (avoid in logs)
 type MaskedString string
@@
 func (ms MaskedString) String() string {
   if len(ms) == 0 {
     return ""
   }
   return Mask(string(ms))
 }
+
+// GoString implements fmt.GoStringer so %#v also prints masked.
+func (ms MaskedString) GoString() string {
+  return ms.String()
+}

127-135: Unmasked JSON/YAML can leak secrets—confirm intended usage.

If structs containing MaskedString are logged via JSON/YAML (structured logs, telemetry), this will emit the raw value. Verify call sites and consider logger integration that formats MaskedString via String()/Text() consciously (e.g., custom slog handler/zap encoder).

string/mask_test.go (4)

31-31: Rename test to canonical initialism.

Use URL, not Url, for consistency with Go initialisms.

-func TestMaskUrl(t *testing.T) {
+func TestMaskURL(t *testing.T) {

154-161: Avoid shadowing json package name.

Rename local var to improve clarity.

-			json, err := ms.MarshalJSON()
+			b, err := ms.MarshalJSON()
 			assert.NoError(t, err)
-			assert.Equal(t, tt.want, string(json))
+			assert.Equal(t, tt.want, string(b))

175-181: Avoid shadowing yaml import.

Minor readability win.

-			yaml, err := ms.MarshalYAML()
+			val, err := ms.MarshalYAML()
 			assert.NoError(t, err)
-			assert.Equal(t, tt.want, yaml)
+			assert.Equal(t, tt.want, val)

233-237: Don’t ignore marshal error in comparison test.

Check error before asserting equality.

-			expectedJSON, _ := json.Marshal(input)
-			assert.Equal(t, expectedJSON, jsonBytes)
+			expectedJSON, err := json.Marshal(input)
+			assert.NoError(t, err)
+			assert.Equal(t, expectedJSON, jsonBytes)
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5db8992 and 65d8b98.

📒 Files selected for processing (3)
  • go.mod (1 hunks)
  • string/mask.go (2 hunks)
  • string/mask_test.go (2 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (go)
🔇 Additional comments (3)
go.mod (1)

30-30: Direct yaml.v3 dependency—LGTM.

Matches new YAML marshal/unmarshal usage in tests.

string/mask.go (2)

4-4: json import—LGTM.

Required for MaskedString.MarshalJSON.


92-99: Delegating MaskArguments to MaskValue—LGTM.

Removes duplication and keeps logic centralized.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (8)
string/string.go (1)

5-8: Ptr returns address of a copy; clarify doc to avoid misuse

Ptr[T] returns a pointer to a copy of v (not the caller’s variable). Tests assuming pointer identity with the caller’s variable will fail; update the doc to make this explicit.

-// Ptr returns a pointer to the given value.
+// Ptr returns a pointer to a copy of v. The returned pointer escapes and is safe to use
+// beyond the call. Note: it is not the address of the caller's variable.
 func Ptr[T any](v T) *T {
 	return &v
 }
string/mask.go (4)

74-90: Tighten URL/JWT detection and simplify control flow

Suggest: (a) broaden URL scheme regex to RFC-compliant set; (b) early returns to reduce nesting.

 func MaskValue(arg string) string {
-	if isURL.MatchString(arg) {
-		u, err := MaskURL(arg)
-		if err == nil {
-			return u
-		} else {
-			return Mask(arg)
-		}
-	} else if isEmail.MatchString(arg) {
-		return MaskEmail(arg)
-	} else if isJWT.MatchString(arg) {
-		return Mask(arg)
-	} else {
-		return arg
-	}
+	if isURL.MatchString(arg) {
+		if u, err := MaskURL(arg); err == nil {
+			return u
+		}
+		return Mask(arg)
+	}
+	if isEmail.MatchString(arg) {
+		return MaskEmail(arg)
+	}
+	if isJWT.MatchString(arg) {
+		return Mask(arg)
+	}
+	return arg
 }

Additionally, consider updating the regex declarations (outside this hunk):

// More permissive per RFC 3986 scheme
var isURL = regexp.MustCompile(`^[A-Za-z][A-Za-z0-9+.-]*://`)
// Base64url without padding is typical; allow optional '=' padding if desired
var isJWT = regexp.MustCompile(`^[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+=*$`)

101-104: Document the security semantics prominently
Call out that fmt/text are masked but JSON/YAML marshal unmasked, so consumers don’t assume “always masked.”

-// MaskedString is a custom string type that masks its value when formatted or text-marshaled.
+// MaskedString masks in fmt (%s/%v/%#v) and TextMarshaler outputs, but intentionally marshals
+// the raw (unmasked) value to JSON and YAML. Do not log JSON/YAML output of this type.
 type MaskedString string

109-112: Minor: avoid extra call in Bytes()
Slightly simpler conversion.

 func (ms MaskedString) Bytes() []byte {
-	return []byte(ms.Text())
+	return []byte(string(ms))
 }

127-135: Unmasked JSON/YAML is intentional — validate usage paths
If these structs are ever JSON/YAML-logged, secrets will leak. Ensure logging paths use fmt/String() or MarshalText for masked output, or wrap log sinks.

Do you want a guardrail utility (e.g., SafeJSON that replaces MaskedString with masked text for logs) and a repository-wide grep to confirm no logging of JSON/YAML using MaskedString-containing types?

sys/result.go (1)

8-13: Consider encapsulating fields to enforce invariant (optional).

Exported Ok/Err allow external code to set both simultaneously. If you want stricter invariants, make fields unexported and expose accessors.

sys/result_test.go (2)

40-63: IsErr happy-path coverage is good. Add a case for nil target.

Add a case like Okint.IsErr(nil) to ensure it returns false after the IsErr hardening.

Apply this diff to add coverage:

 func TestResult_IsErr(t *testing.T) {
@@
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
 			assert.Equal(t, tt.expected, tt.result.IsErr())
 		})
 	}
+
+	t.Run("nil target ignored", func(t *testing.T) {
+		assert.False(t, Ok[int](1).IsErr(nil))
+		assert.False(t, Err[int](errors.New("boom")).IsErr(nil))
+	})
 }

94-108: Add TestErrMatch_OkResult to cover Ok case of IsErrMatches
Ensure that calling IsErrMatches on an Ok result never panics and always returns false, including when no checks are passed.

 func TestErrMatchMultiple(t *testing.T) {
@@
 	assert.Equal(t, err, result.Err)
 }
 
+func TestErrMatch_OkResult(t *testing.T) {
+	ok := Ok("value")
+	assert.False(t, ok.IsErrMatches("value"))
+	assert.False(t, ok.IsErrMatches()) // no checks => false when no error
+}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 65d8b98 and 4eef2e2.

📒 Files selected for processing (8)
  • string/mask.go (2 hunks)
  • string/mask_test.go (2 hunks)
  • string/string.go (1 hunks)
  • string/string_test.go (1 hunks)
  • sys/net.go (1 hunks)
  • sys/net_test.go (1 hunks)
  • sys/result.go (1 hunks)
  • sys/result_test.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • string/mask_test.go
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (go)
🔇 Additional comments (8)
sys/net.go (1)

24-24: Harden localhost detection
Parsing with url.Parse/SplitHostPort plus net.IP.IsLoopback now correctly covers 127/8 and ::1. Note net.IP.IsUnspecified returns true for both “0.0.0.0” and “::” (golangdoc.github.io); the check ip.To4() != nil limits unspecified support to IPv4. Remove that guard to treat “::” as local as well.

string/mask.go (3)

4-4: Import json for custom marshaling — LGTM
Needed for MaskedString.MarshalJSON. No issues.


96-96: Good centralization via MaskValue
Delegating all per-arg masking through MaskValue decreases duplication and keeps rules consistent.


142-145: Constructor — LGTM
Simple and clear; keeps call sites readable.

sys/result.go (2)

15-18: IsOk implementation looks good.


47-56: Constructors Ok/Err are correct and zero-value safe.

sys/result_test.go (2)

10-38: Table tests for IsOk are solid.


65-71: General: tests are readable and comprehensive across types.

Also applies to: 73-79, 81-92, 110-134

Comment thread string/string_test.go Outdated
Comment thread sys/net_test.go Outdated
Comment thread sys/result.go
Comment thread sys/result.go
@jhaynie jhaynie changed the title Add MaskedString Add MaskedString and other utilities Sep 6, 2025

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

♻️ Duplicate comments (1)
sys/result.go (1)

20-40: IsErr hardening looks good (nil fast-path + skip nil checks)

Matches prior guidance: avoids errors.Is with nil and returns early on nil Err.

🧹 Nitpick comments (5)
sys/result.go (1)

42-57: Guard added; consider tiny naming nit in loop

Behavior is correct and the nil guard prevents panics. Minor readability nit: avoid naming the loop var err since it’s a string, not an error.

- for _, err := range checks {
-   if strings.Contains(val, err) {
+ for _, s := range checks {
+   if strings.Contains(val, s) {
      return true
    }
  }
sys/net.go (1)

21-25: Align the comment with actual behavior.

If you keep the broader detection, update the doc to mention loopback ranges (127/8), IPv6 ::1, and unspecified 0.0.0.0/::; also clarify it accepts either a full URL or host[:port].

string/mask_test.go (3)

147-166: Avoid shadowing the encoding/json import with a local variable

Using json as a local name reduces readability and can confuse future edits.

-			json, err := ms.MarshalJSON()
+			b, err := ms.MarshalJSON()
 			assert.NoError(t, err)
-			assert.Equal(t, tt.want, string(json))
+			assert.Equal(t, tt.want, string(b))

168-187: Avoid shadowing the yaml import with a local variable

-			yaml, err := ms.MarshalYAML()
+			v, err := ms.MarshalYAML()
 			assert.NoError(t, err)
-			assert.Equal(t, tt.want, yaml)
+			assert.Equal(t, tt.want, v)

236-246: Don’t ignore json.Marshal errors; also confirm intentional unmasked JSON/YAML semantics

Marshal on strings shouldn’t error, but it’s cheap to assert. Separately, these tests codify that JSON/YAML emit unmasked values; please confirm this is the intended contract for any code paths that might log or externalize these payloads.

-			jsonBytes, err := ms.MarshalJSON()
+			jsonBytes, err := ms.MarshalJSON()
 			assert.NoError(t, err)
-			expectedJSON, _ := json.Marshal(input)
-			assert.Equal(t, expectedJSON, jsonBytes)
+			expJSON, expErr := json.Marshal(input)
+			assert.NoError(t, expErr)
+			assert.Equal(t, expJSON, jsonBytes)
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4eef2e2 and 26e88e7.

📒 Files selected for processing (5)
  • string/mask_test.go (2 hunks)
  • string/string_test.go (1 hunks)
  • sys/net.go (1 hunks)
  • sys/net_test.go (1 hunks)
  • sys/result.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • sys/net_test.go
  • string/string_test.go
🧰 Additional context used
🧬 Code graph analysis (1)
string/mask_test.go (2)
string/mask.go (3)
  • NewMaskedString (143-145)
  • Mask (13-23)
  • MaskedString (102-102)
logger/console.go (1)
  • NewConsoleLogger (245-252)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (go)
🔇 Additional comments (5)
sys/result.go (2)

15-18: IsOk implementation is clear and correct

Simple, idiomatic check against nil error.


59-68: Constructors are ergonomic and correct

Ok initializes with value; Err zeroes T and carries error. API is clear.

string/mask_test.go (3)

189-212: LGTM: Text() returns the raw value as specified

Good coverage across edge cases and Unicode.


267-299: LGTM: Formatting verbs behavior is consistent

%v, %s, and %#v aligning with String() is clearly validated.


455-505: LGTM: JSON/YAML round-trips preserve underlying values and masking semantics

End-to-end behavior is well covered.

Comment thread string/mask_test.go
Comment thread string/mask_test.go
Comment thread sys/net.go Outdated
@coderabbitai
coderabbitai Bot requested a review from robindiddams September 6, 2025 21:16
@jhaynie
jhaynie merged commit 194132b into main Sep 6, 2025
4 of 5 checks passed
@jhaynie
jhaynie deleted the masked-string branch September 6, 2025 21:18

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (8)
sys/net.go (3)

24-35: Normalize whitespace and trailing dot for localhost FQDN.
Trim spaces and accept "localhost." as equivalent to "localhost".

Apply:

   host := url
   if u, err := neturl.Parse(url); err == nil && u.Host != "" {
     host = u.Hostname() // strips [] for IPv6
   } else if h, _, err := net.SplitHostPort(url); err == nil {
     host = h
   } else {
     host = strings.Trim(host, "[]")
   }
+  host = strings.TrimSpace(host)
+  host = strings.TrimSuffix(host, ".")
   if strings.EqualFold(host, "localhost") {
     return true
   }

36-38: Also treat IPv4-mapped loopback (::ffff:127.0.0.1) as local.
Map IPv6-mapped addresses to v4 before checks.

Apply:

-  if ip := net.ParseIP(host); ip != nil {
-    return ip.IsLoopback() || ip.IsUnspecified() // 127/8, ::1, 0.0.0.0, ::
-  }
+  if ip := net.ParseIP(host); ip != nil {
+    if ip4 := ip.To4(); ip4 != nil {
+      ip = ip4
+    }
+    return ip.IsLoopback() || ip.IsUnspecified() // 127/8, ::1, 0.0.0.0, ::
+  }

22-39: Add a couple of negative/edge tests.
To lock behavior:

  • true: "localhost", "LOCALHOST", "localhost:80", "http://localhost", "127.0.0.2", "http://[::1]:443", "http://[::]"
  • true (after refactor above): "http://[::ffff:127.0.0.1]"
  • false: "localhost.evil.com", "http://localhost.evil.com", "127.0.0.1.nip.io", "http://[fe80::1%25en0]"
logger/console_test.go (4)

13-20: Make captureOutput race-safe for parallel tests

log.SetOutput is global; concurrent captureOutput calls (or other tests using the std logger) can interleave. Guard the swap with a mutex.

 func captureOutput(f func()) string {
-	prev := log.Writer()
+	// serialize global std logger redirection
+	testLogMu.Lock()
+	defer testLogMu.Unlock()
+	prev := log.Writer()
 	var buf bytes.Buffer
 	log.SetOutput(&buf)
 	defer log.SetOutput(prev)
 	f()
 	return strings.TrimSpace(buf.String())
 }

Add once in this file (outside the function):

// at top-level in this _test.go file:
var testLogMu sync.Mutex

And import:

import "sync"

14-17: Also restore std logger flags/prefix to fully isolate tests

If code under test tweaks log flags/prefix, restore them to avoid cross-test bleed.

-	prev := log.Writer()
+	prev := log.Writer()
+	prevFlags := log.Flags()
+	prevPrefix := log.Prefix()
 	var buf bytes.Buffer
 	log.SetOutput(&buf)
-	defer log.SetOutput(prev)
+	defer func() {
+		log.SetOutput(prev)
+		log.SetFlags(prevFlags)
+		log.SetPrefix(prevPrefix)
+	}()

110-128: Prefer t.Setenv for test-scoped env vars

Simpler and safer than manual Setenv/Unsetenv, especially on failures.

-		os.Setenv("AGENTUITY_LOG_LEVEL", tt.level)
+		t.Setenv("AGENTUITY_LOG_LEVEL", tt.level)
 		logger := NewConsoleLogger().(*consoleLogger)
@@
-		os.Unsetenv("AGENTUITY_LOG_LEVEL")

19-19: Normalize trailing newlines by using TrimRight
Optional refactor to avoid stripping other whitespace:

- return strings.TrimSpace(buf.String())
+ return strings.TrimRight(buf.String(), "\r\n")

Tests don’t rely on trimming spaces beyond CR/LF.

string/string.go (1)

20-26: Minor tidy: drop else after return

Flatten for readability.

-		if nv == "" {
-			return nil
-		} else {
-			return sys.Ptr(nv)
-		}
+		if nv == "" {
+			return nil
+		}
+		return sys.Ptr(nv)
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 26e88e7 and 858098a.

📒 Files selected for processing (5)
  • logger/console_test.go (1 hunks)
  • string/mask_test.go (2 hunks)
  • string/string.go (2 hunks)
  • sys/net.go (2 hunks)
  • sys/pointer.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • string/mask_test.go
🧰 Additional context used
🧬 Code graph analysis (1)
string/string.go (1)
sys/pointer.go (1)
  • Ptr (10-12)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (go)
🔇 Additional comments (5)
sys/net.go (2)

5-5: Good import alias to avoid shadowing and keep intent clear.
The neturl alias prevents confusion with the url param. All good.


22-39: Secure host parsing replaces brittle substring checks — resolved.
You’re correctly extracting the hostname and using net.IP semantics (loopback/unspecified). This addresses the prior vuln and covers 127/8, ::1, 0.0.0.0, ::.

logger/console_test.go (1)

14-18: Nice: reliably restoring the previous log writer

Saving prev and deferring restoration avoids leaking the test writer even on panic. Good improvement.

sys/pointer.go (1)

9-12: Ptr helper looks good

Returning &v is idiomatic; each call yields a distinct address and avoids the “address of range var” pitfall.

string/string.go (1)

3-7: Importing sys is fine

Dependency looks correct and local; no cycle with sys.

Comment thread string/string.go
Comment on lines 11 to 15
if v != "" {
nv := strings.TrimSpace(v)
if nv == "" {
return nil
} else {
return &nv
}
return sys.Ptr(nv)
}
return nil

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Whitespace-only inputs now return non-nil "" pointer (likely unintended)

This changes behavior and makes StringPointer inconsistent with ClearEmptyStringPointer. If you want empty/whitespace → nil, trim first and check length.

-func StringPointer(v string) *string {
-	if v != "" {
-		nv := strings.TrimSpace(v)
-		return sys.Ptr(nv)
-	}
-	return nil
-}
+func StringPointer(v string) *string {
+	nv := strings.TrimSpace(v)
+	if nv == "" {
+		return nil
+	}
+	return sys.Ptr(nv)
+}

Consider updating the comment to clarify “empty or whitespace-only returns nil; otherwise pointer to trimmed value.”


🏁 Script executed:

#!/bin/bash
set -eux
# Locate and display StringPointer implementation with context
rg -n -C3 "func StringPointer" .

Length of output: 392


🏁 Script executed:

#!/bin/bash
set -eux
# Show full StringPointer function for clarity
rg -n -C3 "func StringPointer" string/string.go

# Search for ClearEmptyStringPointer and related functions in the repo
rg -n -C3 "ClearEmptyStringPointer" .
rg -n -C3 "EmptyStringPointer" .

Length of output: 2001


Refactor StringPointer to return nil for empty or whitespace-only input
Whitespace-only inputs currently produce a non-nil pointer to ""—inconsistent with ClearEmptyStringPointer. Change implementation to trim first and return nil when nv=="".

 func StringPointer(v string) *string {
-   if v != "" {
-       nv := strings.TrimSpace(v)
-       return sys.Ptr(nv)
-   }
-   return nil
+   nv := strings.TrimSpace(v)
+   if nv == "" {
+       return nil
+   }
+   return sys.Ptr(nv)
 }

Also update the doc comment to:

StringPointer returns nil for empty or whitespace-only input; otherwise returns a pointer to the trimmed value.

🤖 Prompt for AI Agents
In string/string.go around lines 11 to 15, the function should trim input first
and return nil for empty or whitespace-only input; update the doc comment to:
"StringPointer returns nil for empty or whitespace-only input; otherwise returns
a pointer to the trimmed value." Modify the implementation to call
strings.TrimSpace(v) into nv, and if nv == "" return nil, otherwise return
sys.Ptr(nv).

Comment thread sys/pointer.go
Comment on lines +3 to +7
import (
"testing"

"github.com/stretchr/testify/assert"
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Remove test-only imports from production file

This drags testing/testify into the build. Move tests to a *_test.go file and drop these imports here.

-import (
-	"testing"
-
-	"github.com/stretchr/testify/assert"
-)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import (
"testing"
"github.com/stretchr/testify/assert"
)
🤖 Prompt for AI Agents
In sys/pointer.go around lines 3 to 7, the production file currently imports
"testing" and "github.com/stretchr/testify/assert", which are test-only; move
any test code using these imports into a new *_test.go file (or existing test
file) and remove the test imports from sys/pointer.go so the production build no
longer depends on testing/testify. Ensure the moved tests import testing and
testify in the test file and that sys/pointer.go only contains production
imports.

Comment thread sys/pointer.go
Comment on lines +14 to +54
func TestPtr(t *testing.T) {
// Test with string
str := "hello"
strPtr := Ptr(str)
assert.NotNil(t, strPtr)
assert.Equal(t, str, *strPtr)

// Test with int
num := 42
numPtr := Ptr(num)
assert.NotNil(t, numPtr)
assert.Equal(t, num, *numPtr)

// Test with bool
flag := true
flagPtr := Ptr(flag)
assert.NotNil(t, flagPtr)
assert.Equal(t, flag, *flagPtr)

// Test with struct
type testStruct struct {
Name string
Age int
}
s := testStruct{Name: "test", Age: 25}
sPtr := Ptr(s)
assert.NotNil(t, sPtr)
assert.Equal(t, s, *sPtr)

// Test with slice
slice := []int{1, 2, 3}
slicePtr := Ptr(slice)
assert.NotNil(t, slicePtr)
assert.Equal(t, slice, *slicePtr)

// Test with nil interface
var nilInterface interface{}
nilPtr := Ptr(nilInterface)
assert.NotNil(t, nilPtr)
assert.Equal(t, nilInterface, *nilPtr)
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Move tests to sys/pointer_test.go

Test code in a non-test file bloats prod deps and won’t run under go test. Remove from this file and add a dedicated test file.

-func TestPtr(t *testing.T) {
-	// Test with string
-	str := "hello"
-	strPtr := Ptr(str)
-	assert.NotNil(t, strPtr)
-	assert.Equal(t, str, *strPtr)
-
-	// Test with int
-	num := 42
-	numPtr := Ptr(num)
-	assert.NotNil(t, numPtr)
-	assert.Equal(t, num, *numPtr)
-
-	// Test with bool
-	flag := true
-	flagPtr := Ptr(flag)
-	assert.NotNil(t, flagPtr)
-	assert.Equal(t, flag, *flagPtr)
-
-	// Test with struct
-	type testStruct struct {
-		Name string
-		Age  int
-	}
-	s := testStruct{Name: "test", Age: 25}
-	sPtr := Ptr(s)
-	assert.NotNil(t, sPtr)
-	assert.Equal(t, s, *sPtr)
-
-	// Test with slice
-	slice := []int{1, 2, 3}
-	slicePtr := Ptr(slice)
-	assert.NotNil(t, slicePtr)
-	assert.Equal(t, slice, *slicePtr)
-
-	// Test with nil interface
-	var nilInterface interface{}
-	nilPtr := Ptr(nilInterface)
-	assert.NotNil(t, nilPtr)
-	assert.Equal(t, nilInterface, *nilPtr)
-}

Add this new file:

// sys/pointer_test.go
package sys

import (
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestPtr(t *testing.T) {
	str := "hello"
	strPtr := Ptr(str)
	assert.NotNil(t, strPtr)
	assert.Equal(t, str, *strPtr)

	num := 42
	numPtr := Ptr(num)
	assert.NotNil(t, numPtr)
	assert.Equal(t, num, *numPtr)

	flag := true
	flagPtr := Ptr(flag)
	assert.NotNil(t, flagPtr)
	assert.Equal(t, flag, *flagPtr)

	type testStruct struct {
		Name string
		Age  int
	}
	s := testStruct{Name: "test", Age: 25}
	sPtr := Ptr(s)
	assert.NotNil(t, sPtr)
	assert.Equal(t, s, *sPtr)

	slice := []int{1, 2, 3}
	slicePtr := Ptr(slice)
	assert.NotNil(t, slicePtr)
	assert.Equal(t, slice, *slicePtr)

	var nilInterface interface{}
	nilPtr := Ptr(nilInterface)
	assert.NotNil(t, nilPtr)
	assert.Equal(t, nilInterface, *nilPtr)
}

Run to ensure no prod file imports test deps:


🏁 Script executed:

#!/bin/bash
rg -nP --type=go -g '!**/*_test.go' -C2 '^\s*import\s+"testing"'
rg -nP --type=go -g '!**/*_test.go' -C2 'github.com/stretchr/testify/assert'

Length of output: 281


Move TestPtr out of sys/pointer.go into sys/pointer_test.go
sys/pointer.go must not import testing or github.com/stretchr/testify/assert or contain any test code—tests belong in _test.go files. Remove the following from sys/pointer.go (lines 14–54):

-func TestPtr(t *testing.T) {
-	// Test with string
-	str := "hello"
-	strPtr := Ptr(str)
-	assert.NotNil(t, strPtr)
-	assert.Equal(t, str, *strPtr)
-
-	// Test with int
-	num := 42
-	numPtr := Ptr(num)
-	assert.NotNil(t, numPtr)
-	assert.Equal(t, num, *numPtr)
-
-	// Test with bool
-	flag := true
-	flagPtr := Ptr(flag)
-	assert.NotNil(t, flagPtr)
-	assert.Equal(t, flag, *flagPtr)
-
-	// Test with struct
-	type testStruct struct {
-		Name string
-		Age  int
-	}
-	s := testStruct{Name: "test", Age: 25}
-	sPtr := Ptr(s)
-	assert.NotNil(t, sPtr)
-	assert.Equal(t, s, *sPtr)
-
-	// Test with slice
-	slice := []int{1, 2, 3}
-	slicePtr := Ptr(slice)
-	assert.NotNil(t, slicePtr)
-	assert.Equal(t, slice, *slicePtr)
-
-	// Test with nil interface
-	var nilInterface interface{}
-	nilPtr := Ptr(nilInterface)
-	assert.NotNil(t, nilPtr)
-	assert.Equal(t, nilInterface, *nilPtr)
-}

And add a new file sys/pointer_test.go containing:

package sys

import (
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestPtr(t *testing.T) {
	str := "hello"
	strPtr := Ptr(str)
	assert.NotNil(t, strPtr)
	assert.Equal(t, str, *strPtr)

	num := 42
	numPtr := Ptr(num)
	assert.NotNil(t, numPtr)
	assert.Equal(t, num, *numPtr)

	flag := true
	flagPtr := Ptr(flag)
	assert.NotNil(t, flagPtr)
	assert.Equal(t, flag, *flagPtr)

	type testStruct struct {
		Name string
		Age  int
	}
	s := testStruct{Name: "test", Age: 25}
	sPtr := Ptr(s)
	assert.NotNil(t, sPtr)
	assert.Equal(t, s, *sPtr)

	slice := []int{1, 2, 3}
	slicePtr := Ptr(slice)
	assert.NotNil(t, slicePtr)
	assert.Equal(t, slice, *slicePtr)

	var nilInterface interface{}
	nilPtr := Ptr(nilInterface)
	assert.NotNil(t, nilPtr)
	assert.Equal(t, nilInterface, *nilPtr)
}
🤖 Prompt for AI Agents
In sys/pointer.go around lines 14–54 you have test code (TestPtr) and imports
for testing/testify which must be removed; delete the entire TestPtr function
and any testing/testify imports from sys/pointer.go, then create a new file
sys/pointer_test.go with package sys that imports "testing" and
"github.com/stretchr/testify/assert" and contains the TestPtr function as shown
in the review comment so tests live in a _test.go file and production code no
longer imports testing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant