Skip to content

✨refactor: remove userID, add external secure hasher to Auth API call#2293

Merged
mkmccarty merged 1 commit intomainfrom
mm-branch-3
Mar 30, 2026
Merged

✨refactor: remove userID, add external secure hasher to Auth API call#2293
mkmccarty merged 1 commit intomainfrom
mm-branch-3

Conversation

@mkmccarty
Copy link
Copy Markdown
Owner

No description provided.

Copilot AI review requested due to automatic review settings March 30, 2026 19:34
@mkmccarty mkmccarty merged commit 159700f into main Mar 30, 2026
11 checks passed
@mkmccarty mkmccarty deleted the mm-branch-3 branch March 30, 2026 19:34
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Refactors the Egg Inc authenticated API call path by removing the explicit userID parameter and introducing an external “secure hasher” binary to generate the AuthenticatedMessage.code for requests.

Changes:

  • Added getSecureHash() which shells out to ./secure_hasher to compute the auth code.
  • Updated APIAuthenticatedCall signature and changed how it populates AuthenticatedMessage (removes user_id, sets code, and changes message contents).
  • Updated leaderboard API usage to the new APIAuthenticatedCall signature.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 6 comments.

File Description
src/ei/ei_api.go Introduces external-hasher execution and changes AuthenticatedMessage request-envelope construction.
src/ei/ei_api_leaderboard.go Updates call site to match the new APIAuthenticatedCall signature.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +512 to +522
binaryPath := "./secure_hasher"
info, err := os.Stat(binaryPath)
if err != nil {
if os.IsNotExist(err) {
return "", fmt.Errorf("secure hasher binary not found at %s", binaryPath)
}
return "", fmt.Errorf("failed to stat secure hasher binary: %w", err)
}
if info.IsDir() {
return "", fmt.Errorf("secure hasher path is a directory: %s", binaryPath)
}
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

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

getSecureHash hard-codes a relative path (./secure_hasher). This is brittle in production (depends on current working directory) and makes deployments harder to reason about. Consider making the hasher path configurable (config/env/flag) and/or resolving it to an absolute path once at startup, rather than re-stat'ing a relative path on every call.

Copilot uses AI. Check for mistakes.
Comment on lines +524 to +534
cmd := exec.Command(binaryPath)

// Pipe the raw bytes into the binary's stdin
cmd.Stdin = bytes.NewReader(data)

var out bytes.Buffer
cmd.Stdout = &out

if err := cmd.Run(); err != nil {
return "", fmt.Errorf("failed to execute hasher: %w", err)
}
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

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

exec.Command(binaryPath) is run without any timeout/cancellation. If the external hasher hangs (or blocks on stdin), APIAuthenticatedCall will hang the request path. Use exec.CommandContext with a reasonable timeout (e.g., a few seconds) so the API call can fail fast and the bot remains responsive.

Copilot uses AI. Check for mistakes.
Comment on lines +532 to +533
if err := cmd.Run(); err != nil {
return "", fmt.Errorf("failed to execute hasher: %w", err)
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

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

On hasher failures, the error returned from cmd.Run() won't include any stderr output, which can make diagnosing deployment issues difficult. Consider capturing cmd.Stderr (buffer) and including it in the returned error when execution fails.

Suggested change
if err := cmd.Run(); err != nil {
return "", fmt.Errorf("failed to execute hasher: %w", err)
var errBuf bytes.Buffer
cmd.Stderr = &errBuf
if err := cmd.Run(); err != nil {
return "", fmt.Errorf("failed to execute hasher: %w; stderr: %s", err, errBuf.String())

Copilot uses AI. Check for mistakes.
return "", fmt.Errorf("failed to execute hasher: %w", err)
}

return strings.TrimSpace(out.String()), nil
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

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

getSecureHash returns whatever the external process writes to stdout after trimming whitespace, but APIAuthenticatedCall assumes it's a valid auth code. Add basic validation (e.g., non-empty, expected hex length/charset) and return an error if the output is malformed to avoid sending bad auth requests upstream.

Suggested change
return strings.TrimSpace(out.String()), nil
hash := strings.TrimSpace(out.String())
if hash == "" {
return "", fmt.Errorf("secure hasher returned empty output")
}
// Validate that the output looks like a hex-encoded hash (e.g., SHA-256).
for i := 0; i < len(hash); i++ {
c := hash[i]
if !((c >= '0' && c <= '9') ||
(c >= 'a' && c <= 'f') ||
(c >= 'A' && c <= 'F')) {
return "", fmt.Errorf("secure hasher returned non-hex character %q", c)
}
}
// Enforce an expected hash length (64 characters is typical for SHA-256).
if len(hash) != 64 {
return "", fmt.Errorf("secure hasher returned unexpected length %d", len(hash))
}
return hash, nil

Copilot uses AI. Check for mistakes.
Comment on lines 555 to 558
authMsg := &AuthenticatedMessage{
Message: []byte(enc.EncodeToString(innerBin)),
UserId: &userID,
Message: innerBin,
Code: &secureHash,
}
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

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

This change alters the request envelope semantics: AuthenticatedMessage.Message used to contain base64-encoded innerBin and UserId was populated; now the raw protobuf bytes are sent and user_id is omitted in favor of code. Because this is an external API contract, please ensure the server expects raw message bytes (not base64 text) and does not require user_id; otherwise these calls will fail at runtime. If both formats must be supported, consider keeping backward-compatible fields or gating behavior by endpoint/version.

Copilot uses AI. Check for mistakes.
Comment on lines 539 to 558
@@ -517,9 +546,15 @@ func APIAuthenticatedCall(reqURL string, userID string, request proto.Message) [
return nil
}

secureHash, err := getSecureHash(innerBin)
if err != nil {
log.Print(err)
return nil
}

authMsg := &AuthenticatedMessage{
Message: []byte(enc.EncodeToString(innerBin)),
UserId: &userID,
Message: innerBin,
Code: &secureHash,
}
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

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

APIAuthenticatedCall now depends on executing an external binary (secure_hasher), but there are no tests covering the request-envelope creation (hash generation + AuthenticatedMessage fields). Since there are existing tests in this package, consider adding a unit test that verifies APIAuthenticatedCall builds the correct AuthenticatedMessage (ideally by injecting/mocking the hash function so tests don't shell out).

Copilot uses AI. Check for mistakes.
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.

2 participants