Skip to content

fix: strict DID matching in get_profile - #298

Open
euxaristia wants to merge 4 commits into
Gitlawb:mainfrom
euxaristia:fix/get-profile-strict-did-matching
Open

fix: strict DID matching in get_profile#298
euxaristia wants to merge 4 commits into
Gitlawb:mainfrom
euxaristia:fix/get-profile-strict-did-matching

Conversation

@euxaristia

@euxaristia euxaristia commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Summary

get_profile used a broad SQL suffix match (did LIKE '%:' || $1) when resolving profiles by short id. That can match a different DID method that happens to share the same trailing id, so the wrong profile row can be returned (or the query can fail when multiple rows match).

This aligns profile lookup with the existing get_repo / normalize_owner_key contract:

  • strip did:key: only when the remainder is a bare key id
  • leave other DID methods intact
  • match via a SQL CASE expression (PROFILE_DID_CASE_SQL) instead of a wildcard suffix

Test plan

  • Added get_profile_does_not_match_non_key_did (sqlx): bare short id and did:key: resolve the key-form profile; a non-key DID with the same suffix resolves only itself
  • CI: cargo test -p gitlawb-node
  • CI: fmt / clippy

Notes

Same pattern as the owner-key hardening already applied to repository lookup. Code-only change; no migration.

Summary by CodeRabbit

  • Bug Fixes

    • Improved profile lookup accuracy for key-based identifiers.
    • Prevented profiles with key-based and non-key identifiers from being incorrectly treated as the same profile.
    • Ensured profile updates and CID changes work correctly through supported identifier aliases.
    • Added coverage to verify distinct profile matching behavior.
  • Tests

    • Improved cross-platform test reliability by limiting Unix-specific filesystem checks to supported systems.

Replace the broad LIKE suffix match in profile resolution with the same
normalize_owner_key / CASE pattern already used by get_repo, so non-key
DID methods that share a short-id suffix cannot resolve the wrong profile.

Add a sqlx test locking in bare, did:key, and non-key DID behavior.
@github-actions github-actions Bot added needs-issue PR has no linked issue needs-tests Source changed without accompanying tests (advisory) labels Aug 1, 2026
@github-actions

github-actions Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Thanks for the contribution. A couple of things will help us review this faster:

  • Link the issue this addresses (Closes #123). For protocol changes, open an issue first.
  • This changes Rust source but no tests changed. Tests are required for fixes and strongly encouraged for features.

See CONTRIBUTING.md. Update the PR and these notes will clear automatically.

@coderabbitai

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

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

Next review available in: 55 minutes

Your organization has reached its usage spending cap. Adjust your spending cap in the billing tab.

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 Plus

Run ID: a18320ea-d1b9-45db-b08e-aac8b6022d6c

📥 Commits

Reviewing files that changed from the base of the PR and between c83cbc5 and b4016dc.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (3)
  • crates/gitlawb-node/src/db/mod.rs
  • crates/gitlawb-node/src/git/repo_store.rs
  • crates/gitlawb-node/src/sync.rs
📝 Walkthrough

Walkthrough

Profile operations now use did:key-aware normalized matching. Upserts preserve stored DID values when called through aliases. Integration tests cover method isolation and normalization parity. Symlink- and permission-based tests now compile only on Unix.

Changes

Profile DID lookup

Layer / File(s) Summary
Normalize and match profile DIDs
crates/gitlawb-node/src/db/mod.rs
Adds normalized SQL matching for profile lookup, upsert, and CID updates. Upserts preserve the stored DID representation.
Validate DID method separation
crates/gitlawb-node/src/db/mod.rs
Adds integration tests for aliases, method isolation, CID updates, and Rust/SQL normalization parity.

Unix test guards

Layer / File(s) Summary
Guard repository containment tests
crates/gitlawb-node/src/git/repo_store.rs
Compiles symlink-based containment tests only on Unix.
Guard synchronization tests
crates/gitlawb-node/src/sync.rs
Compiles symlink, permission, fixture, helper, and starvation tests only on Unix.

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

Possibly related PRs

  • Gitlawb/node#274: Adds Unix-only guards for symlink-related tests introduced by this code area.
  • Gitlawb/node#276: Shares DID normalization and alias handling in crates/gitlawb-node/src/db/mod.rs.

Suggested labels: kind:bug

Suggested reviewers: beardthelion

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description explains the bug and test plan, but it omits several required template sections and leaves issue, validation, and checklist items incomplete. Complete the required template sections, including motivation and issue link, change details, verification commands, review checklist, and protocol impact.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the primary change: strict DID matching in profile lookup.
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

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
Contributor

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 current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/gitlawb-node/src/db/mod.rs`:
- Around line 3518-3531: Update upsert_profile to use the normalized existing
profile identity when performing updates: after resolving the row with
normalize_owner_key and PROFILE_DID_CASE_SQL, bind existing.did rather than the
raw input did. Preserve insert behavior for new profiles, and add regression
coverage that upserts a bare stored profile via its full did:key: form and
verifies the persisted fields are updated.
🪄 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 Plus

Run ID: 99f20673-ce5d-46da-948b-7939dda95e85

📥 Commits

Reviewing files that changed from the base of the PR and between c83cbc5 and 13b565b.

📒 Files selected for processing (1)
  • crates/gitlawb-node/src/db/mod.rs

Comment thread crates/gitlawb-node/src/db/mod.rs
@beardthelion beardthelion added the crate:node gitlawb-node — the serving node and REST API label Aug 1, 2026
@euxaristia

Copy link
Copy Markdown
Contributor Author

🦁

get_profile equates bare short ids with did:key forms. upsert_profile
must UPDATE by existing.did so a did:key: alias against a bare-stored
profile actually persists. set_profile_cid uses the same CASE match.
Add a regression test covering both write paths.
@euxaristia

Copy link
Copy Markdown
Contributor Author

Addressed CodeRabbit's finding in 9836c3a:

  • upsert_profile now updates by existing.did (the stored row identity) after get_profile resolves a bare / did:key: alias, and returns that stored form.
  • set_profile_cid uses the same PROFILE_DID_CASE_SQL match so a full did:key: form updates a bare-stored profile.
  • Added upsert_profile_updates_via_did_key_alias covering both write paths.

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

@euxaristia, acknowledged. I will perform a full review of the updated pull request.


If you found this review helpful, would you consider giving us a shout-out on X?

Thank you for using CodeRabbit!

✅ Action performed

Full review finished.


Your included review limit is currently reached under our Fair Usage Limits Policy. This review may still proceed through usage-based billing if eligible. Your next included review will be available in 46 minutes.

cargo audit fails on ruint 1.17.2 (incorrect overflow flags / truncated
shift amounts). Upgrade the lockfile to the patched 1.20.0 release.
@euxaristia

Copy link
Copy Markdown
Contributor Author

@beardthelion pls review 💪

@beardthelion beardthelion left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

The get_profile CASE + normalize_owner_key contract matches get_repo: bare and did:key alias, non-key methods stay distinct. I reverted get_profile to the old LIKE matcher against a did:gitlawb-only row and bare short resolved that row; with CASE it returns None. CodeRabbit's write-path ask landed: gutting the existing.did UPDATE bind leaves display_name at "before", and gutting set_profile_cid's CASE leaves profile_cid unset. Both shipped sqlx tests are green on this head after those checks.

Findings

  • [P2] Pin the cross-method deny with a lone non-key fixture
    crates/gitlawb-node/src/db/mod.rs:4698
    get_profile_does_not_match_non_key_did seeds both a bare row and a did:gitlawb twin, then asserts bare short returns the bare row. Under the old LIKE matcher Postgres can still return the bare row first, so that assertion can stay green while the bug the PR names is live. Add a case with only the non-key row present and assert get_profile(bare) is None (and keep the dual-row / did:key alias coverage you already have).

  • [P2] Pin PROFILE_DID_CASE_SQL with the owner-key boundary matrix
    crates/gitlawb-node/src/db/mod.rs:922
    normalize_owner_key_matches_sql_case only exercises OWNER_KEY_CASE_SQL. PROFILE_DID_CASE_SQL is a hand-renamed twin on column did, and a drift there would reopen the same class silently. Run the same boundary VALUES list through PROFILE_DID_CASE_SQL (alias the column did) and assert Rust normalize_owner_key agrees.

Not an ask, recorded only: get_repo still carries ORDER BY / LIMIT 1 for mirror preference and get_profile does not. Auth cannot plant bare+did:key dual rows today, so I am not asking for a preference clause on this round. agents.rs short-id resolve stays method-blind; profile GET does not go through it.

…ary matrix

Add a lone non-key DID fixture check to get_profile_does_not_match_non_key_did to verify that a bare short query returns None when only a non-key profile exists. Add profile_did_case_sql_matches_normalize_owner_key to pin PROFILE_DID_CASE_SQL against Rust normalize_owner_key across the full owner-key boundary matrix. Add cfg(unix) target gates to Unix-specific symlink and permission test helpers.

Refs Gitlawb#298
@euxaristia

Copy link
Copy Markdown
Contributor Author

Addressed review findings in b4016dc:

  • \get_profile_does_not_match_non_key_did\ now seeds a lone non-key DID profile first to assert \get_profile(bare)\ returns \None\ before seeding the bare key profile row.
  • Added \profile_did_case_sql_matches_normalize_owner_key\ to pin \PROFILE_DID_CASE_SQL\ across the full owner-key boundary matrix.
  • Added #[cfg(unix)]\ target gates to Unix-specific symlink and permission test helpers.

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

@euxaristia, I will perform a full review of the updated pull request.

✅ Action performed

Full review finished.


Your included review limit is currently reached under our Fair Usage Limits Policy. This review may still proceed through usage-based billing if eligible. Your next included review will be available in 59 minutes.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
crates/gitlawb-node/src/db/mod.rs (1)

920-923: 🚀 Performance & Scalability | 🔵 Trivial

Verify index usage for PROFILE_DID_CASE_SQL.

PROFILE_DID_CASE_SQL wraps agent_profiles.did in a CASE. The new read and CID-update predicates compare that expression to a parameter. A normal B-tree index on did will not normally support this expression. Full-DID lookups can therefore regress to sequential scans.

Run EXPLAIN with production-like data. If the table can grow, add a matching expression index or preserve an indexable exact/alias branch.

🤖 Prompt for 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.

In `@crates/gitlawb-node/src/db/mod.rs` around lines 920 - 923, Verify the query
plans for predicates using PROFILE_DID_CASE_SQL with production-like
agent_profiles data via EXPLAIN, checking whether the did index is used for
full-DID lookups. If scans occur and the table can grow, add a matching
expression index or retain an indexable exact/alias predicate branch while
preserving normalized-key matching.
🤖 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 `@crates/gitlawb-node/src/db/mod.rs`:
- Around line 3461-3463: Update the agent_profiles schema/migration flow to
canonicalize equivalent profile DIDs and prevent both bare IDs and their did:key
aliases from coexisting. Add a de-duplication migration for existing rows, then
enforce uniqueness on the normalized DID value (or consistently canonicalize all
inserts) so get_profile and upsert_profile cannot select or update different
matching rows.

---

Nitpick comments:
In `@crates/gitlawb-node/src/db/mod.rs`:
- Around line 920-923: Verify the query plans for predicates using
PROFILE_DID_CASE_SQL with production-like agent_profiles data via EXPLAIN,
checking whether the did index is used for full-DID lookups. If scans occur and
the table can grow, add a matching expression index or retain an indexable
exact/alias predicate branch while preserving normalized-key matching.
🪄 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 Plus

Run ID: 9c46183e-7568-44fa-a2b6-6193c7977cf4

📥 Commits

Reviewing files that changed from the base of the PR and between 13b565b and b4016dc.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (3)
  • crates/gitlawb-node/src/db/mod.rs
  • crates/gitlawb-node/src/git/repo_store.rs
  • crates/gitlawb-node/src/sync.rs

Comment thread crates/gitlawb-node/src/db/mod.rs
@beardthelion
beardthelion dismissed their stale review August 1, 2026 19:32

Superseded by re-review of b4016dc; prior asks landed.

@beardthelion beardthelion left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Both prior asks landed on b4016dc and are load-bearing. Reverting get_profile to the old LIKE matcher reds the lone non-key fixture. Drifting PROFILE_DID_CASE_SQL reds the new boundary matrix. Gutting the existing.did UPDATE bind and set_profile_cid's CASE still red the alias write test. CI is green on this head and the base is current with main.

Findings

(none)

Not an ask, recorded only: agent_profiles still keys the raw did PK, so bare and did:key: siblings can coexist and fetch_optional stays plan-order under that state. Auth writes only the did:key form, so the API cannot plant that pair today. Same residual as last round. Repos already carry an expression index for the twin CASE; profiles do not. Table is small enough that I am not asking for the migration on this PR.

@kevincodex1 this is ready to merge.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

crate:node gitlawb-node — the serving node and REST API needs-issue PR has no linked issue needs-tests Source changed without accompanying tests (advisory)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants