Skip to content

fix: propagate crypto/rand failure out of GenerateSecureToken - #371

Open
mrofreP wants to merge 1 commit into
UndernetIRC:masterfrom
mrofreP:fix/generate-secure-token-swallowed-error
Open

mrofreP wants to merge 1 commit into
UndernetIRC:masterfrom
mrofreP:fix/generate-secure-token-swallowed-error

Conversation

@mrofreP

@mrofreP mrofreP commented Jul 18, 2026

Copy link
Copy Markdown

GenerateSecureToken logged its error and returned the value anyway:

func GenerateSecureToken(length int) string {
    str, err := CryptoRandomString(int64(length))
    if err != nil {
        logger.Error("failed to generate secure token: ", "error", err)
    }
    return str
}

CryptoRandomString returns ("", err) on any failure -- it fills a buffer
of the full length and bails on the first CryptoRandomInt error -- so a
dead entropy source yields an empty string, not a short one. Callers
treated that "" as a valid secret:

  • RequestManagerChange stored Crc: "" and emailed a confirmation URL
    with an empty ?token=, then returned 201. The request was permanently
    unconfirmable and nothing said so. The only signal was an unrelated
    panic on confirmationToken[:8] further down, removed on another branch.

  • generateSingleBackupCode already returned (string, error) but always
    returned nil, so a failure produced the backup code "-" for the user
    to write down.

  • UserRegister stored an empty activation cookie and mailed an
    activation link that could never work.

  • CreateToken (reset manager) inferred the failure from an
    "if token == ''" check instead of a real error.

Change GenerateSecureToken to return (string, error) and handle it at
all four call sites: the two controllers return 500 rather than
reporting success for an account or request nobody can ever activate,
and the reset manager propagates the real cause.

Keep an explicit empty-token guard in CreateToken. The error return
covers a crypto/rand failure, but GenerateSecureToken(0) returns
("", nil) -- a non-positive TokenLength (misconfiguration) still yields
an empty token with no error, and an empty reset token must never be
stored.

Tests: cover GenerateSecureToken's zero-length case, and add a
CreateToken subtest asserting a zero TokenLength is rejected before any
token row is created. Negative lengths are not tested: they reach
make([]byte, length) in CryptoRandomString and panic before any error
can be returned, a separate pre-existing issue.

GenerateSecureToken logged its error and returned the value anyway:

    func GenerateSecureToken(length int) string {
        str, err := CryptoRandomString(int64(length))
        if err != nil {
            logger.Error("failed to generate secure token: ", "error", err)
        }
        return str
    }

CryptoRandomString returns ("", err) on any failure -- it fills a buffer
of the full length and bails on the first CryptoRandomInt error -- so a
dead entropy source yields an empty string, not a short one. Callers
treated that "" as a valid secret:

- RequestManagerChange stored Crc: "" and emailed a confirmation URL
  with an empty ?token=, then returned 201. The request was permanently
  unconfirmable and nothing said so. The only signal was an unrelated
  panic on confirmationToken[:8] further down, removed on another branch.

- generateSingleBackupCode already returned (string, error) but always
  returned nil, so a failure produced the backup code "-" for the user
  to write down.

- UserRegister stored an empty activation cookie and mailed an
  activation link that could never work.

- CreateToken (reset manager) inferred the failure from an
  "if token == ''" check instead of a real error.

Change GenerateSecureToken to return (string, error) and handle it at
all four call sites: the two controllers return 500 rather than
reporting success for an account or request nobody can ever activate,
and the reset manager propagates the real cause.

Keep an explicit empty-token guard in CreateToken. The error return
covers a crypto/rand failure, but GenerateSecureToken(0) returns
("", nil) -- a non-positive TokenLength (misconfiguration) still yields
an empty token with no error, and an empty reset token must never be
stored.

Tests: cover GenerateSecureToken's zero-length case, and add a
CreateToken subtest asserting a zero TokenLength is rejected before any
token row is created. Negative lengths are not tested: they reach
make([]byte, length) in CryptoRandomString and panic before any error
can be returned, a separate pre-existing issue.
@codacy-production

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics 0 complexity · 0 duplication

Metric Results
Complexity 0
Duplication 0

View in Codacy

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

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