fix(contract): add access control to set_risk_tier — closes #50#74
Merged
mericcintosun merged 2 commits intoJul 14, 2026
Conversation
…tier Fixes mericcintosun#50 - Add initialize(admin) function that sets a trusted admin address once - set_risk_tier now requires caller to be admin OR the user themselves - update_chosen_tier now requires the user themselves (require_auth) - Add get_admin() helper for transparency - Add 7 new auth tests covering all access paths: - test_initialize_sets_admin - test_initialize_twice_panics - test_admin_can_set_risk_tier_for_any_user - test_user_can_set_own_risk_tier - test_third_party_cannot_set_risk_tier_for_another_user - test_user_can_update_own_chosen_tier - test_third_party_cannot_update_chosen_tier - Refactor all existing tests to use setup() helper that calls initialize() Signed-off-by: Daniel Job Gonsum <danieljob003@gmail.com>
|
@jobbykings is attempting to deploy a commit to the mericcintosun Team on Vercel. A member of the Team first needs to authorize it. |
…tier Fixes mericcintosun#50 Problem: set_risk_tier had no authorization check — any address could overwrite any user's risk score, making the credit system trivially manipulable and blocking mainnet deployment. Solution: - Add initialize(admin) — sets a trusted admin address once; panics on re-initialization - Split set_risk_tier into two entry points: - set_risk_tier: user.require_auth() — user signs for themselves - admin_set_risk_tier: admin.require_auth() — oracle/backend flow - update_chosen_tier: user.require_auth() — only the user themselves - Add get_admin() transparency helper - Extract write_risk_tier() private helper to avoid duplication - Add 5 new auth tests: - test_initialize_sets_admin - test_initialize_twice_panics - test_user_can_set_own_risk_tier - test_third_party_cannot_set_risk_tier_for_another_user - test_admin_can_set_risk_tier_for_any_user - test_non_admin_cannot_call_admin_set_risk_tier - test_user_can_update_own_chosen_tier - test_third_party_cannot_update_chosen_tier - Refactor all existing tests to use setup() helper (initialize first) Note: env.invoker() does not exist in the Soroban SDK. The correct pattern is address.require_auth() on the specific principal that must sign. The 'admin OR user' pattern is implemented as two separate functions with distinct auth requirements. Signed-off-by: Daniel Job Gonsum <danieljob003@gmail.com>
This was referenced Jul 14, 2026
mericcintosun
added a commit
that referenced
this pull request
Jul 15, 2026
…isk-score bug - All 6 active gaps re-verified against current code (still open) - New: risk model returned (1 - p) * 100 while the weights already yield P(risky), so risky wallets received LOW scores -> found, fixed, moved to resolved - Flag the model recalibration as a behaviour change needing product review - Record resolved: contract auth (#74), CSP (verified live), Vercel build, repo artifact hygiene - Note API contract bug: invalid sort returns 500 instead of 400 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
mericcintosun
added a commit
that referenced
this pull request
Jul 15, 2026
… work
Closes the two gaps that made the risk score meaningless: the browser both
computed AND submitted its own score, and the write path never worked.
Server-side risk oracle
- New POST /api/risk/attest + src/lib/server/riskOracle.js: the score is
re-derived from Horizon data the SERVER fetches (src/lib/server/horizonMetrics.js,
mirroring the client formulas) and written with admin_set_risk_tier, signed by
the contract admin key. A user can no longer self-report a score.
- The transaction goes through prepareTransaction() (simulation), which attaches
the footprint, resource fees and the authorization entries that
admin.require_auth() needs — the old client path skipped simulation entirely
and silently fell back to writing account manageData instead of the contract.
- Verified against testnet: tx 3f983a678bf191d0ed7b790d507b7f14a80e55314ff18cd7382e2d0d4caefb00
-> on-chain {"score":57,"tier":"TIER_2"}, can_access_tier(TIER_2) = true.
Rate limiting moved server-side
- The 24h cooldown is now enforced from the contract's own timestamp: stateless,
and no longer bypassable by clearing localStorage. Verified: second call -> 429.
Contract actually deployed with auth
- PR #74 added require_auth to the source, but the chain still ran the old
unauthenticated contract — and both recorded contract ids no longer existed on
testnet at all (reset), so every read/write was failing.
- Deployed CCGZV37C3FC2GLVNIHFEC6OVDHRFLQCELPTQLII44Z7RXZBEER5POPRO and ran
initialize(admin). Auth verified on-chain: an unauthorized set_risk_tier is
rejected. Replaced the dead contract id across 7 files (env, CI, docs).
Removed the self-service write path
- src/app/lib/writeScore.js deleted: every export had zero callers once both
call sites moved to the oracle. page.js previously wrote a score derived from
values the USER typed into a form. This also removes one "kalepail" public-seed
usage. src/lib/writeScore.js (dead set_score caller) deleted too.
Bug fix
- AutomatedRiskAnalyzer never awaited the async checkRateLimit and stored the
Promise in state, so rateLimitStatus.canUpdate was always undefined and the
"Update Score" button rendered permanently disabled.
Tests 170/170, lint and build green. gaps.md updated: 6 active gaps -> 3.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes #50 — [Security]
set_risk_tierhas no access control — any address can overwrite any user's scoreWithout authorization checks, any on-chain actor could call
set_risk_tierto manipulate any user's credit score, making the entire system untrustworthy and blocking mainnet deployment.Changes
risk_score/src/lib.rsinitialize(admin)get_admin()set_risk_tier— auth guardrequire_auth()update_chosen_tier— auth guardrequire_auth()setup()test helperinitialize+mock_all_authsso all existing tests keep passingNew tests (7)
test_initialize_sets_admintest_initialize_twice_panicstest_admin_can_set_risk_tier_for_any_usertest_user_can_set_own_risk_tiertest_third_party_cannot_set_risk_tier_for_another_user← key security testtest_user_can_update_own_chosen_tiertest_third_party_cannot_update_chosen_tier← key security testAll 18 pre-existing tests were updated to use the
setup()helper and continue to pass.Testing