Skip to content

feat: include SHA-256 hash prefix in redacted secrets (FR-9)#12

Merged
thebtf merged 3 commits into
mainfrom
feat/redaction-hash
Mar 17, 2026
Merged

feat: include SHA-256 hash prefix in redacted secrets (FR-9)#12
thebtf merged 3 commits into
mainfrom
feat/redaction-hash

Conversation

@thebtf
Copy link
Copy Markdown
Owner

@thebtf thebtf commented Mar 17, 2026

Summary

  • RedactSecrets() now outputs [REDACTED:{hash[:8]}] instead of [REDACTED]
  • Hash matches auto:{hash} vault entry name from ExtractSecrets()
  • Enables reverse lookup from redacted text to vault entry

Test plan

  • RedactSecrets("sk-abc123...")sk-a...[REDACTED:c99e03e4]
  • RedactSecrets("api_key=abc123...")api_key=[REDACTED:586f23e7]
  • Hash in [REDACTED:xxx] matches auto:xxx from ExtractSecrets()
  • All 39 privacy tests pass

Summary by CodeRabbit

Примечания к выпуску

  • Исправления ошибок

    • Улучшена маскировка секретов: маркеры теперь включают детерминированный хеш‑префикс ([REDACTED:<хеш>]), сохраняется видимая часть ключа и корректно обрабатываются значения с разделителями (= или :); для длинных значений отображается также усечённая часть.
  • Тесты

    • Добавлены и расширены тесты, подтверждающие согласованность хеш‑префиксов между извлечением секретов и их маскировкой.

RedactSecrets() now outputs [REDACTED:{hash[:8]}] instead of plain
[REDACTED], using the same SHA-256 hash derivation as ExtractSecrets().
This allows correlating redacted text with vault entries without
exposing the secret value.

Added TestRedactSecretsHashMatchesExtract to verify hash consistency
between RedactSecrets and ExtractSecrets across all secret patterns.
@gemini-code-assist
Copy link
Copy Markdown

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the privacy module by introducing a mechanism to correlate redacted secrets with their original extracted forms. By embedding a SHA-256 hash prefix into the redaction marker, the system can now uniquely identify and link redacted values to their corresponding vault entries, which greatly aids in debugging and auditing without compromising sensitive data. This change improves the traceability of secrets throughout the system.

Highlights

  • Enhanced Secret Redaction: The RedactSecrets() function now includes an 8-character SHA-256 hash prefix in the redaction marker (e.g., [REDACTED:c99e03e4]).
  • Improved Correlation: This hash prefix enables reverse lookup and correlation between redacted text and the corresponding auto:{hash} vault entries generated by ExtractSecrets(), without exposing the actual secret.
  • New Test Coverage: A new test, TestRedactSecretsHashMatchesExtract, was added to ensure that the hash in the redacted output consistently matches the hash used in the ExtractSecrets() output.
Changelog
  • internal/privacy/secrets.go
    • Updated the RedactSecrets function's documentation to reflect the new hash-inclusion behavior.
    • Modified the RedactSecrets function to calculate an 8-character SHA-256 hash prefix of the secret value and embed it into the redaction marker.
  • internal/privacy/secrets_test.go
    • Imported the strings package for string manipulation in tests.
    • Updated existing TestRedactSecrets cases to expect the new hash-prefixed redaction format.
    • Added a new test function, TestRedactSecretsHashMatchesExtract, to verify that the hash in redacted output matches the hash used in ExtractSecrets names.
Activity
  • No human activity has occurred on this pull request yet.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 17, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 3d89de8a-8f3d-45f7-a263-af4069806576

📥 Commits

Reviewing files that changed from the base of the PR and between 2a9d406 and 7eb25a2.

📒 Files selected for processing (2)
  • internal/privacy/secrets.go
  • internal/privacy/secrets_test.go

Walkthrough

Добавлена общая функция deriveHashPrefix для вычисления 8‑символьного SHA‑256 префикса из значения секрета; ExtractSecrets и RedactSecrets перешли на её использование. RedactSecrets теперь вставляет маркеры вида [REDACTED:<hash>], корректно обрабатывает разделители =/: и отображает усечённые префиксы для длинных значений. Тесты обновлены для проверки согласованности хешей.

Changes

Cohort / File(s) Summary
Редактор секретов
internal/privacy/secrets.go
Добавлена функция deriveHashPrefix(match string); ExtractSecrets использует её для формирования имён auto:<prefix>; RedactSecrets генерирует маркеры [REDACTED:<hash>], выбирает первый встречный разделитель (= или :) для сохранения ключа, вставляет маркер после разделителя или как автономный маркер; поведение для длинных значений — отображение префикса и усечения. Комментарии обновлены.
Тесты редактирования
internal/privacy/secrets_test.go
Обновлены ожидаемые результаты тестов для включения [REDACTED:<hash>]; переименованы тесты; добавлен TestRedactSecretsHashMatchesExtract, проверяющий, что префиксы хешей в редактировании соответствуют префиксам в именах, возвращаемых ExtractSecrets.

Sequence Diagram(s)

(нет диаграмм — изменения не вводят новую многокомпонентную последовательность взаимодействий)

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Поэма

🐰 Нора шуршит — кусочек кода с хвостиком,
Восьмёрка хеша мелькнёт, как яркий луч.
[REDACTED:abcd1234] — безопасно и тихо,
Я моргну ушком и спряту секрет в луч.
Поскребу лапкой — всё в порядке, тишь да благодать.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 57.14% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: adding SHA-256 hash prefix to redacted secrets, which aligns with the core modifications in secrets.go and tests.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/redaction-hash
📝 Coding Plan
  • Generate coding plan for human review comments

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 golangci-lint (2.11.3)

Error: can't load config: unsupported version of the configuration: "" See https://golangci-lint.run/docs/product/migration-guide for migration instructions
The command is terminated due to an error: can't load config: unsupported version of the configuration: "" See https://golangci-lint.run/docs/product/migration-guide for migration instructions


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

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request enhances secret redaction by including a SHA-256 hash prefix, allowing for cross-referencing with extracted secrets. The implementation is straightforward and includes a valuable new test to ensure the hashes match between ExtractSecrets and RedactSecrets. My review includes a couple of suggestions to improve code maintainability and test robustness.

Comment thread internal/privacy/secrets.go Outdated
Comment thread internal/privacy/secrets_test.go Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
internal/privacy/secrets.go (1)

131-137: Вынесите вычисление hashPrefix в общий helper для ExtractSecrets и RedactSecrets.

Сейчас одна и та же деривация хеша продублирована в двух местах; это риск расхождения поведения при следующих правках.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/privacy/secrets.go` around lines 131 - 137, The hash prefix
derivation for secrets is duplicated; create a single helper (e.g.,
deriveHashPrefix or computeHashPrefix) that takes the secret string (after using
extractSecretValue(match) or the original match) and returns the 8-char SHA-256
hex prefix, then replace the inline logic in both ExtractSecrets and
RedactSecrets to call that helper so both functions share identical hashing
behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@internal/privacy/secrets_test.go`:
- Line 160: The test calls t.Run(input[:20], ...) which can panic for inputs
shorter than 20; change the test to compute a safe sub-name before calling t.Run
by checking len(input) and using input[:20] only when len(input) > 20 (e.g.,
name := input; if len(name) > 20 { name = name[:20] }; then t.Run(name, func(t
*testing.T) { ... } ) ), ensuring the slice operation on input is protected;
update the occurrence of the raw slice inside the t.Run call (the fragment
input[:20]) accordingly.

In `@internal/privacy/secrets.go`:
- Around line 140-145: The current logic checks for "=" before ":" which can
pick an "=" inside the secret value (e.g., password: "a=b") and leak part of the
secret; update the code in internal/privacy/secrets.go that handles the local
variable match to find the first key/value separator by computing both indexes
(idxEq := strings.Index(match, "=") and idxColon := strings.Index(match, ":"))
and then choosing the smallest non-negative index (prefer the earlier separator
in the string) to redact from match[:idx+1] + "[REDACTED:"+hashPrefix+"]";
replace the two separate if-blocks with this single selection so you always cut
at the actual key/value separator rather than the first "=" encountered inside a
value.

---

Nitpick comments:
In `@internal/privacy/secrets.go`:
- Around line 131-137: The hash prefix derivation for secrets is duplicated;
create a single helper (e.g., deriveHashPrefix or computeHashPrefix) that takes
the secret string (after using extractSecretValue(match) or the original match)
and returns the 8-char SHA-256 hex prefix, then replace the inline logic in both
ExtractSecrets and RedactSecrets to call that helper so both functions share
identical hashing behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 31d59518-b722-44e9-9543-cf6ed32d18e7

📥 Commits

Reviewing files that changed from the base of the PR and between d78640d and ef02745.

📒 Files selected for processing (2)
  • internal/privacy/secrets.go
  • internal/privacy/secrets_test.go

Comment thread internal/privacy/secrets_test.go Outdated
Comment thread internal/privacy/secrets.go Outdated
@thebtf
Copy link
Copy Markdown
Owner Author

thebtf commented Mar 17, 2026

@codex review

…ash derivation

- Extract deriveHashPrefix() helper to eliminate duplicated sha256+hex logic
  between ExtractSecrets and RedactSecrets; both functions now share a single
  consistent hash derivation path.
- Fix CRITICAL separator-selection bug in RedactSecrets: previous logic
  always checked '=' before ':', so a value like password: "a=b" would use
  the '=' inside the value as the cut point, leaking part of the secret.
  New logic picks min(idxEq, idxColon) to always cut at the first separator.
- Fix test fragility: t.Run(input[:20], ...) panics for inputs shorter than
  20 chars; replaced with safe length-guarded name computation.
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@internal/privacy/secrets.go`:
- Around line 58-61: При нормализации переменной value в secrets.go избегать
использования extractSecretValue для случаев с ":"-присваиванием, потому что
extractSecretValue отдаёт приоритет "=", что обрезает значения, содержащие "="
внутри секрета; вместо этого, если match содержит ":" (т.е. присваивание через
двоеточие), присвоить value как часть справа от первого ':' (с нужной обрезкой
пробелов/кавычек), иначе вызывать extractSecretValue(match); сохранять текущую
fallback-логику (если value == "" — установить value = match). Используйте
идентификаторы match, value и функцию extractSecretValue при правке.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: daf2275b-2c23-43c6-bafc-0013a7193fee

📥 Commits

Reviewing files that changed from the base of the PR and between ef02745 and 2a9d406.

📒 Files selected for processing (2)
  • internal/privacy/secrets.go
  • internal/privacy/secrets_test.go

Comment on lines +58 to +61
value := extractSecretValue(match)
if value == "" {
value = match
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Неверная нормализация value для hash при :-присваивании со знаком = внутри секрета

Сейчас hash может считаться по усечённому хвосту значения (например, password: "abc=def..."), потому что extractSecretValue приоритетно ищет "=". Это ломает стабильность auto:{hash} и может дать ложную дедупликацию разных секретов.

Предлагаемое исправление
+func firstKVSeparatorIndex(match string) int {
+	idxEq := strings.Index(match, "=")
+	idxColon := strings.Index(match, ":")
+	switch {
+	case idxEq == -1:
+		return idxColon
+	case idxColon == -1:
+		return idxEq
+	case idxEq < idxColon:
+		return idxEq
+	default:
+		return idxColon
+	}
+}
+
 func extractSecretValue(match string) string {
-	for _, sep := range []string{"=", ":"} {
-		if idx := strings.Index(match, sep); idx != -1 {
-			val := strings.TrimSpace(match[idx+1:])
-			val = strings.Trim(val, `'"`)
-			if val != "" {
-				return val
-			}
-		}
-	}
+	idx := firstKVSeparatorIndex(match)
+	if idx == -1 {
+		return ""
+	}
+	val := strings.TrimSpace(match[idx+1:])
+	val = strings.Trim(val, `'"`)
+	if val != "" {
+		return val
+	}
 	return ""
 }
📝 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
value := extractSecretValue(match)
if value == "" {
value = match
}
func firstKVSeparatorIndex(match string) int {
idxEq := strings.Index(match, "=")
idxColon := strings.Index(match, ":")
switch {
case idxEq == -1:
return idxColon
case idxColon == -1:
return idxEq
case idxEq < idxColon:
return idxEq
default:
return idxColon
}
}
func extractSecretValue(match string) string {
- for _, sep := range []string{"=", ":"} {
- if idx := strings.Index(match, sep); idx != -1 {
- val := strings.TrimSpace(match[idx+1:])
- val = strings.Trim(val, `'"`)
- if val != "" {
- return val
- }
- }
idx := firstKVSeparatorIndex(match)
if idx == -1 {
return ""
}
val := strings.TrimSpace(match[idx+1:])
val = strings.Trim(val, `'"`)
if val != "" {
return val
}
return ""
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/privacy/secrets.go` around lines 58 - 61, При нормализации
переменной value в secrets.go избегать использования extractSecretValue для
случаев с ":"-присваиванием, потому что extractSecretValue отдаёт приоритет "=",
что обрезает значения, содержащие "=" внутри секрета; вместо этого, если match
содержит ":" (т.е. присваивание через двоеточие), присвоить value как часть
справа от первого ':' (с нужной обрезкой пробелов/кавычек), иначе вызывать
extractSecretValue(match); сохранять текущую fallback-логику (если value == "" —
установить value = match). Используйте идентификаторы match, value и функцию
extractSecretValue при правке.

@thebtf thebtf merged commit 1aa26fb into main Mar 17, 2026
1 of 2 checks passed
@thebtf thebtf deleted the feat/redaction-hash branch March 17, 2026 22:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant