Skip to content

.pr_agent_auto_best_practices

root edited this page Sep 23, 2025 · 3 revisions

Pattern 1: Always short-circuit and return early on invalid preconditions or error states (e.g., non-success HTTP responses, missing prerequisite blocks, expired deadlines) before proceeding to parse, compute, or persist.

Example code before:

let res = http_client.get(url).await?;
// Parse body before checking status
let body = res.text().await?;
if res.status().is_client_error() {
    warn!("client error: {}", body);
}
process_body(body)?;

Example code after:

let res = http_client.get(url).await?;
let status = res.status();
if status.is_client_error() || status.is_server_error() {
    let body = res.text().await.unwrap_or_default();
    error!("http error {}: {}", status, body);
    return Err(AppError::Http(status));
}
let body = res.text().await?;
process_body(body)?;
Relevant past accepted suggestions:
Suggestion 1:

Early-return on HTTP errors

Short-circuit on non-success HTTP statuses before attempting to read or interpret the body to avoid misusing error responses as content.

crates/bcr-ebill-api/src/external/identity_proof.rs [109-143]

 match self.cl.post(proxy_url).json(&proxy_req).send().await {
     Ok(res) => {
         let status = res.status();
+        if status.is_client_error() {
+            let body = res.text().await.unwrap_or_default();
+            error!("Error checking url: {url} for identity proof: {status}, {body}");
+            return IdentityProofStatus::FailureClient;
+        }
+        if status.is_server_error() {
+            let body = res.text().await.unwrap_or_default();
+            error!("Error checking url: {url} for identity proof: {status}, {body}");
+            return IdentityProofStatus::FailureServer;
+        }
         match res.text().await {
             Ok(body) => {
-                if status.is_client_error() {
-                    error!(
-                        "Error checking url: {url} for identity proof: {status}, {body}"
-                    );
-                    return IdentityProofStatus::FailureClient;
-                } else if status.is_server_error() {
-                    error!(
-                        "Error checking url: {url} for identity proof: {status}, {body}"
-                    );
-                    return IdentityProofStatus::FailureServer;
-                }
-
-                // Check if the identity proof is contained in the response
                 if identity_proof.is_contained_in(&body) {
                     IdentityProofStatus::Success
                 } else {
                     IdentityProofStatus::NotFound
                 }
             }
             Err(body_err) => {
                 error!("Error checking url: {url} for identity proof: {body_err}");
                 IdentityProofStatus::FailureClient
             }
         }
     }
     Err(req_err) => {
         error!("Error checking url: {url} for identity proof: {req_err}");
         IdentityProofStatus::FailureConnect
     }
 }

Suggestion 2:

Handle missing RequestToPay block

The code is missing a check for when there's no RequestToPay block. In that case, the function continues execution but may encounter issues later. You should add an early return when no RequestToPay block exists, as payment checking is only relevant when a payment has been requested.

crates/bcr-ebill-api/src/service/bill_service/payment.rs [37-50]

 if let Some(req_to_pay) =
     chain.get_last_version_block_with_op_code(BillOpCode::RequestToPay)
 {
     let deadline_base = get_deadline_base_for_req_to_pay(req_to_pay, &bill)?;
     // deadline has expired - don't need to check payment
     if util::date::check_if_deadline_has_passed(
         deadline_base,
         now,
         PAYMENT_DEADLINE_SECONDS,
     ) {
         info!("Payment deadline for bill {bill_id} expired - not checking");
         return Ok(());
     }
+} else {
+    // No RequestToPay block exists, so no payment to check
+    info!("No payment request found for bill {bill_id} - not checking");
+    return Ok(());
 }

Suggestion 3:

Missing return after deadline check

The function is missing a return statement after detecting that the payment deadline has expired. This could lead to unnecessary processing of expired payment requests.

crates/bcr-ebill-api/src/service/bill_service/payment.rs [42-48]

 if util::date::check_if_deadline_has_passed(
     deadline_base,
     now,
     PAYMENT_DEADLINE_SECONDS,
 ) {
     info!("Payment deadline for bill {bill_id} expired");
+    return Ok(());
 }

Suggestion 4:

Check block addition success

The function ignores the return value of chain.try_add_block(block.clone()), which returns a boolean indicating success or failure. If the block addition fails but doesn't invalidate the chain, this error will be silently ignored, and the function will proceed to save an invalid block.

crates/bcr-ebill-transport/src/handler/bill_chain_event_handler.rs [113-132]

 async fn add_bill_blocks(&self, bill_id: &str, blocks: Vec<BillBlock>) -> Result<()> {
     if let Ok(mut chain) = self.bill_blockchain_store.get_chain(bill_id).await {
         for block in blocks {
-            chain.try_add_block(block.clone());
+            let block_added = chain.try_add_block(block.clone());
+            if !block_added {
+                error!("Failed to add block to chain for bill {bill_id}");
+                return Err(Error::BlockChain(
+                    "Failed to add block to chain".to_string(),
+                ));
+            }
             if !chain.is_chain_valid() {
-                error!("Received block is not valid for bill {bill_id}");
+                error!("Chain became invalid after adding block for bill {bill_id}");
                 return Err(Error::BlockChain(
-                    "Received bill block is not valid".to_string(),
+                    "Chain became invalid after adding block".to_string(),
                 ));
             }
             self.save_block(bill_id, █).await?
         }
         Ok(())
     } else {
         error!("Failed to get chain for received bill block {bill_id}");
         Err(Error::BlockChain(
             "Failed to get chain for bill".to_string(),
         ))
     }
 }

Suggestion 5:

Missing identity validation

The active method doesn't validate if the identity exists before returning it. If the personal identity node ID is invalid or doesn't exist in the system, this could lead to returning an invalid identity. Add a check to verify the identity exists before returning it.

crates/bcr-ebill-pwa/src/api/identity.rs [27-41]

 #[wasm_bindgen]
 pub async fn active(&self) -> Result<JsValue> {
     let current_identity = get_current_identity();
+    
+    // Verify that the identity exists
+    if !get_ctx().identity_service.identity_exists().await {
+        return Err(Error::NotFound.into());
+    }
+    
     let (node_id, t) = match current_identity.company {
         None => (current_identity.personal, IdentityType::Person),
         Some(company_node_id) => (company_node_id, IdentityType::Company),
     };
     let switch_identity = SwitchIdentity {
         t: Some(t.into_web()),
         node_id,
     };
     let res = serde_wasm_bindgen::to_value(&switch_identity)?;
     Ok(res)
 }

Pattern 2: Validate state transitions and inputs explicitly before mutating or persisting (e.g., check for duplicates, confirm addition success, verify identity existence, correct argument order).

Example code before:

// Add item without checks
state.items.push(item.clone());
repo.save(item)?;

Example code after:

// Validate before mutating/persisting
if state.items.contains(&item) {
    return Err(AppError::Duplicate);
}
if !service.input_is_valid(&item) {
    return Err(AppError::InvalidInput);
}
state.items.push(item.clone());
if !state.try_commit() {
    return Err(AppError::CommitFailed);
}
repo.save(item)?
Relevant past accepted suggestions:
Suggestion 1:

Prevent duplicate signatory additions

The AddSignatory operation doesn't check for duplicate signatories before adding. This could lead to the same signatory being added multiple times, which may cause inconsistent state or unexpected behavior in the company management system.

crates/bcr-ebill-core/src/company.rs [71-73]

-pub fn apply_block_data(&mut self, data: &CompanyBlockPayload) {
-    match data {
-        CompanyBlockPayload::Update(payload) => {
-            self.name = payload.name.to_owned().unwrap_or(self.name.to_owned());
-            self.email = payload.email.to_owned().unwrap_or(self.email.to_owned());
-            ...
-        }
-        CompanyBlockPayload::AddSignatory(payload) => {
-            self.signatories.push(payload.signatory.to_owned());
-        }
-        CompanyBlockPayload::RemoveSignatory(payload) => {
-            self.signatories.retain(|i| i != &payload.signatory);
-        }
-        _ => {}
+CompanyBlockPayload::AddSignatory(payload) => {
+    if !self.signatories.contains(&payload.signatory) {
+        self.signatories.push(payload.signatory.to_owned());
     }
 }

Suggestion 2:

Check block addition success

The function ignores the return value of chain.try_add_block(block.clone()), which returns a boolean indicating success or failure. If the block addition fails but doesn't invalidate the chain, this error will be silently ignored, and the function will proceed to save an invalid block.

crates/bcr-ebill-transport/src/handler/bill_chain_event_handler.rs [113-132]

 async fn add_bill_blocks(&self, bill_id: &str, blocks: Vec<BillBlock>) -> Result<()> {
     if let Ok(mut chain) = self.bill_blockchain_store.get_chain(bill_id).await {
         for block in blocks {
-            chain.try_add_block(block.clone());
+            let block_added = chain.try_add_block(block.clone());
+            if !block_added {
+                error!("Failed to add block to chain for bill {bill_id}");
+                return Err(Error::BlockChain(
+                    "Failed to add block to chain".to_string(),
+                ));
+            }
             if !chain.is_chain_valid() {
-                error!("Received block is not valid for bill {bill_id}");
+                error!("Chain became invalid after adding block for bill {bill_id}");
                 return Err(Error::BlockChain(
-                    "Received bill block is not valid".to_string(),
+                    "Chain became invalid after adding block".to_string(),
                 ));
             }
             self.save_block(bill_id, █).await?
         }
         Ok(())
     } else {
         error!("Failed to get chain for received bill block {bill_id}");
         Err(Error::BlockChain(
             "Failed to get chain for bill".to_string(),
         ))
     }
 }

Suggestion 3:

Missing identity validation

The active method doesn't validate if the identity exists before returning it. If the personal identity node ID is invalid or doesn't exist in the system, this could lead to returning an invalid identity. Add a check to verify the identity exists before returning it.

crates/bcr-ebill-pwa/src/api/identity.rs [27-41]

 #[wasm_bindgen]
 pub async fn active(&self) -> Result<JsValue> {
     let current_identity = get_current_identity();
+    
+    // Verify that the identity exists
+    if !get_ctx().identity_service.identity_exists().await {
+        return Err(Error::NotFound.into());
+    }
+    
     let (node_id, t) = match current_identity.company {
         None => (current_identity.personal, IdentityType::Person),
         Some(company_node_id) => (company_node_id, IdentityType::Company),
     };
     let switch_identity = SwitchIdentity {
         t: Some(t.into_web()),
         node_id,
     };
     let res = serde_wasm_bindgen::to_value(&switch_identity)?;
     Ok(res)
 }

Suggestion 4:

Fix incorrect key combination order

The get_combined_bitcoin_key function is called with incorrect argument order - the caller's key should be passed after the bill key, not before. This could cause incorrect key combination and potential security issues.

crates/bcr-ebill-api/src/service/bill_service.rs [1850-1854]

 let private_key = self.bitcoin_client.get_combined_private_key(
-    &caller_keys.get_bitcoin_private_key(get_config().bitcoin_network()),
     &BcrKeys::from_private_key(&bill_keys.private_key)?
         .get_bitcoin_private_key(get_config().bitcoin_network()),
+    &caller_keys.get_bitcoin_private_key(get_config().bitcoin_network()),
 )?;

[Auto-generated best practices - 2025-09-23]

Clone this wiki locally