Skip to content

feat(wallet): migrate Uphold OAuth secrets to gitignored Uphold-Info.plist#798

Open
romchornyi wants to merge 5 commits into
masterfrom
chore/secrets-hardening
Open

feat(wallet): migrate Uphold OAuth secrets to gitignored Uphold-Info.plist#798
romchornyi wants to merge 5 commits into
masterfrom
chore/secrets-hardening

Conversation

@romchornyi

@romchornyi romchornyi commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Issue being fixed or feature implemented

This change removes the mechanism that caused the Uphold credential leak in PR-797.

The old Release build-phase swap copied DWUpholdMainnetConstants__Release.m over the tracked DWUpholdMainnetConstants.m, which meant a Release build could rewrite the source file with real secrets. This migration moves Uphold OAuth credentials to a gitignored runtime plist so the tracked source no longer contains or receives secret values.

What was done?

  • Reworked DWUpholdMainnetConstants.m so Uphold mainnet client_id, client_secret, and authorizeURLFormat are read from a gitignored Uphold-Info.plist at runtime.
  • Updated DWUpholdConstants.m so the Uphold testnet client_secret is also read from the same plist under SANDBOX_CLIENT_SECRET.
  • Removed the “Uphold Constants” and “Cleanup Uphold Constants” run-script build phases from both dashwallet and dashpay.
  • Removed the clang-format special case for DWUpholdMainnetConstants.m.
  • Deleted the now-unused DWUpholdMainnetConstants__Empty.m.
  • Added **/Uphold-Info.plist to .gitignore and added the plist to Copy Bundle Resources.
  • Updated CLAUDE.md to document the new secrets-management pattern for Uphold.

How Has This Been Tested?

  • Ran plutil -lint on DashWallet/Uphold-Info.plist.
  • Ran xcodebuild -list -project DashWallet.xcodeproj to verify the project still parses correctly after the pbxproj changes.
  • Ran gitleaks on the staged changes during commit creation; no leaks were found.
  • Verified the tracked Uphold source files no longer contain the secret values directly.

Breaking Changes

None.

Checklist:

  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have added or updated relevant unit/integration/functional/e2e tests
  • I have made corresponding changes to the documentation

For repository code-owners and collaborators only

  • I have assigned this pull request to a milestone

Summary by CodeRabbit

  • Security

    • Added automated secret scanning for pull requests and protected branch updates.
    • Added optional local commit checks to help prevent accidental credential leaks.
    • Removed embedded OAuth credentials and moved secure configuration to protected app resources.
    • Added guidance for credential handling, rotation, and reviewed exceptions.
  • Build & Configuration

    • Updated application configuration and project resources to support the new secure credential setup.
    • Added protection for sensitive production configuration files.

…ret-scan guardrails

Remove the redundant Coinbase CLIENT_ID/CLIENT_SECRET from project.pbxproj build
settings and both Info.plist files. These were never read at runtime (Coinbase
reads gitignored Coinbase-Info.plist) yet sat committed as a leak vector.

Add defense-in-depth against future secret leaks:
- CI secret scan (.github/workflows/gitleaks.yml) — non-bypassable backstop
- local pre-commit hook (.githooks/pre-commit) — opt-in via core.hooksPath
- gitleaks config/allowlist (.gitleaks.toml, .gitleaksignore)
- Secrets Management rule documented in CLAUDE.md

Note: leaked credentials already in git history must still be rotated; this
only stops re-introduction.
…fo.plist

Removes the Release build-phase swap that copied DWUpholdMainnetConstants__Release.m over the tracked constants file — the root cause of the PR-797 Uphold credential leak. Mainnet client_id / client_secret / authorize URL now read from a gitignored Uphold-Info.plist at runtime (same pattern as Coinbase / ZenLedger / Topper). The previously hardcoded sandbox client_secret is likewise moved out of source into the plist under SANDBOX_CLIENT_SECRET. No secret value remains in any tracked file.

- Remove "Uphold Constants" / "Cleanup Uphold Constants" run-script build phases from both dashwallet and dashpay targets
- Remove the clang-format special-case for DWUpholdMainnetConstants.m
- Delete the now-unused DWUpholdMainnetConstants__Empty.m
- Add **/Uphold-Info.plist to .gitignore; add the plist to Copy Bundle Resources
- DWUpholdMainnetConstants.m + DWUpholdConstants.m read credentials from the plist
- Update CLAUDE.md Secrets Management to reference Uphold-Info.plist

Credentials were rotated during incident response; this removes the mechanism so the leak cannot recur.
@coderabbitai

coderabbitai Bot commented Jul 14, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@romchornyi, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 15 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 69b33c48-1239-4919-bda1-e22997efc950

📥 Commits

Reviewing files that changed from the base of the PR and between 9854bb0 and 90e4433.

📒 Files selected for processing (3)
  • .gitleaks.toml
  • CLAUDE.md
  • DashWallet.xcodeproj/project.pbxproj
📝 Walkthrough

Walkthrough

The change adds local and CI gitleaks enforcement, removes tracked Uphold credentials, loads runtime values from Uphold-Info.plist, and updates Xcode project references, build phases, sources, and resources.

Changes

Secret handling and Uphold configuration

Layer / File(s) Summary
Secret scanning enforcement
.githooks/pre-commit, .github/workflows/gitleaks.yml, .gitleaks.toml, .gitleaksignore, .gitignore, CLAUDE.md
Adds staged-change and CI gitleaks scans, custom detection rules, allowlists, ignored secret-file patterns, and repository secret-handling guidance.
Uphold credential externalization
DashPay/dashpay-info.plist, DashWallet/Info.plist, DashWallet.xcodeproj/project.pbxproj, DashWallet/Sources/Models/Uphold/*
Removes OAuth values from tracked configuration and loads Uphold client values from Uphold-Info.plist.
Xcode project wiring
DashWallet.xcodeproj/project.pbxproj
Updates file references, source/resource membership, DashSpend and gift-card entries, build phases, placeholders, and clang-format scripts.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant App
  participant DWUpholdConstants
  participant UpholdInfoPlist
  App->>DWUpholdConstants: request client ID or secret
  DWUpholdConstants->>UpholdInfoPlist: read keyed value from bundle
  UpholdInfoPlist-->>DWUpholdConstants: return plist string or empty string
  DWUpholdConstants-->>App: return configuration value
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: moving Uphold OAuth secrets into a gitignored Uphold-Info.plist.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch chore/secrets-hardening

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/gitleaks.yml:
- Around line 17-20: Update the actions/checkout@v4 step to explicitly disable
credential persistence by setting persist-credentials to false, while preserving
fetch-depth: 0 for the full-history secret scan.

In @.gitleaks.toml:
- Around line 22-27: Update the hardcoded-hex-secret-literal regex in
.gitleaks.toml to use the inline case-insensitive flag (?i), while preserving
the existing 40–64 character length bounds and assignment/return patterns.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: d7b143ab-0b00-4321-a688-3e54c7d714d1

📥 Commits

Reviewing files that changed from the base of the PR and between b1e024b and 9854bb0.

📒 Files selected for processing (12)
  • .githooks/pre-commit
  • .github/workflows/gitleaks.yml
  • .gitignore
  • .gitleaks.toml
  • .gitleaksignore
  • CLAUDE.md
  • DWUpholdMainnetConstants__Empty.m
  • DashPay/dashpay-info.plist
  • DashWallet.xcodeproj/project.pbxproj
  • DashWallet/Info.plist
  • DashWallet/Sources/Models/Uphold/DWUpholdConstants.m
  • DashWallet/Sources/Models/Uphold/DWUpholdMainnetConstants.m
💤 Files with no reviewable changes (3)
  • DashWallet/Info.plist
  • DashPay/dashpay-info.plist
  • DWUpholdMainnetConstants__Empty.m

Comment thread .github/workflows/gitleaks.yml Outdated
Comment thread .gitleaks.toml
…nning

The gitleaks/gitleaks-action@v2 wrapper now requires a paid license for organization repos, so on the dashpay org it failed on every run ("missing gitleaks license") without ever scanning. Remove the CI workflow and rely on the local .githooks/pre-commit gitleaks hook instead.

- Delete .github/workflows/gitleaks.yml
- Update CLAUDE.md Secrets Management to document local-only enforcement (pre-commit hook), noting the CI scan was intentionally dropped
…tive

The hardcoded-hex-secret-literal rule matched only lowercase [0-9a-f], so an uppercase hex secret (e.g. return @"DA72F…") would slip through undetected. Add the (?i) flag so hex literals are caught regardless of casing. 24-char pbxproj UUIDs remain excluded by the {40} / {40,64} length bounds, not the character class (verified: uppercase and lowercase 40-hex literals flagged, 24-char UUID not).
@romchornyi

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 14, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

abaranouski
abaranouski previously approved these changes Jul 16, 2026
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.

3 participants