Skip to content

fix(add-factor): lookup ensure vs delete + heal retries - #235

Open
orekav wants to merge 5 commits into
uaf/4-add-factor-hardeningfrom
uaf/5-lookup-ensure-heal-followups
Open

fix(add-factor): lookup ensure vs delete + heal retries#235
orekav wants to merge 5 commits into
uaf/4-add-factor-hardeningfrom
uaf/5-lookup-ensure-heal-followups

Conversation

@orekav

@orekav orekav commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Summary

Follow-ups from Codex review on #234:

  • Avoid resurrecting lookups after concurrent deletion: ensure_main_factor_lookup re-reads metadata (and again before retry insert) and skips restore if the factor/backup is gone.
  • Retry failed lookup healing: rollback heal attempts up to 3 times, with owner verification on ConditionalCheckFailed, before logging exhaustion.

Stacked on #234 (PR 5/5 of the universal-add-factor split).

Test plan

  • cargo clippy --all-targets -- -D warnings
  • cargo test --lib routes::add_factor
  • cargo test --test add_factor_happy_paths test_add_factor_same_oidc_single_session

Re-read metadata before ensuring FactorLookup so concurrent deletes do
not resurrect stale rows, and retry rollback heal attempts before giving
up on restoring a concurrent writer's lookup.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 65928c0b9e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/routes/add_factor.rs Outdated
backup_id: &str,
new_factor_kind: &FactorKind,
) -> Result<(), ErrorResponse> {
match backup_storage.get_metadata_by_backup_id(backup_id).await? {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Retry metadata checks before abandoning lookup repair

If this new metadata read exhausts an S3/AWS SDK transient error after the factor write committed, ? returns immediately without attempting the lookup insertion. In the race this helper is intended to repair—another add-factor request owned the lookup row and deleted it after this request committed metadata—the result is a factor stored in metadata with no lookup entry, so recovery cannot find the backup; the consumed challenge also makes a transparent client replay unlikely. The new metadata checks, including the retry-path check below, need retry/reconciliation behavior rather than abandoning the repair on the first read error.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 908c463

Metadata presence checks now retry (up to 3×). If they remain unavailable we still attempt the lookup insert (FactorPresence::Unknown) instead of aborting repair with ?.

Do not abandon lookup repair on the first S3 metadata error; retry the
presence check and, if still unknown, proceed with insert so a committed
factor is not left untraceable.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 908c463a3b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/routes/add_factor.rs
Comment on lines +665 to +669
match factor_present_in_metadata_with_retry(
backup_storage,
backup_id,
new_factor_kind,
&factor_pk,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Serialize the metadata check with lookup restoration

When /delete-factor removes the factor from S3 after this presence check but deletes the DynamoDB row before the subsequent insert, ensure_main_factor_lookup recreates the lookup after deletion has completed. The result is a stale mapping that routes recovery to metadata without the factor and prevents the identity from being registered elsewhere; the new pre-read therefore does not actually close the deletion race without coordination, a tombstone/version check, or equivalent post-insert reconciliation.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Agreed this needs a post-insert reconcile rather than only tightening the pre-check.

Follow-up: #237 — after a successful ensure insert/adopt, re-read metadata and delete the lookup if the factor is gone (closes the delete-between-check-and-insert resurrection window).

Comment thread src/routes/add_factor.rs Outdated
Comment on lines +567 to +568
if attempt == MAX_ATTEMPTS {
return;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve the immediate heal retry after the row vanishes

When the first heal insert returns ConditionalCheckFailed and the consistent lookup then returns None, the next two attempts re-read S3 before retrying the DynamoDB insert. If those metadata reads transiently fail, this return abandons healing even though the first attempt already confirmed that the factor exists; unlike the parent implementation's immediate retry, the final state is a committed factor with no lookup row. The fresh evidence in this revision is the new terminal return after the retry loop's metadata errors.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in bfe42ae on this PR.

On ConditionalCheckFailed + consistent None, heal now retries the Dynamo insert immediately (without requiring another S3 metadata re-read). Once metadata has confirmed the factor is present, later transient S3 failures also continue insert attempts instead of abandoning heal.

When ConditionalCheckFailed is followed by a consistent miss, retry Dynamo
insert immediately (and keep inserting if metadata was already confirmed)
so a transient S3 failure cannot abandon heal.
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