Halborn Logo

Octopus Network Anchor - Octopus Network


Prepared by:

Halborn Logo

HALBORN

Last Updated 04/26/2024

Date of Engagement by: March 8th, 2022 - April 24th, 2022

Summary

92% of all REPORTED Findings have been addressed

All findings

12

Critical

0

High

0

Medium

6

Low

5

Informational

1


1. INTRODUCTION

Octopus Network engaged Halborn to conduct a security assessment on their NEAR smart contracts beginning on March 8th, 2022 and ending April 24th, 2022. Octopus Network is a multichain interoperable cryptonetwork for launching and running Web3.0 Substrate-based application-specific blockchains, aka appchains.

Though this security audit's outcome is satisfactory, only the most essential aspects were tested and verified to achieve objectives and deliverables set in the scope due to time and resource constraints. It is essential to note the use of the best practices for secure development.

2. AUDIT SUMMARY

The team at Halborn was provided six weeks for the engagement and assigned one full-time security engineer to audit the security of the assets in scope. The engineer is a blockchain and smart contract security expert with advanced penetration testing, smart-contract hacking, and deep knowledge of multiple blockchain protocols.

The purpose of this audit is to achieve the following:

    • Identify potential security issues within the NEAR smart contracts.

In summary, Halborn identified few security risks that were mostly addressed by the Octopus Network team.

3. TEST APPROACH & METHODOLOGY

Halborn performed a combination of manual view of the code and automated security testing to balance efficiency, timeliness, practicality, and accuracy in regard to the scope of the smart contract audit. While manual testing is recommended to uncover flaws in logic, process, and implementation; automated testing techniques help enhance coverage of smart contracts and can quickly identify items that do not follow security best practices. The following phases and associated tools were used throughout the term of the audit:

    • Research into architecture, purpose, and use of the platform.

    • Manual code read and walkthrough.

    • Manual Assessment of use and safety for the critical Rust variables and functions in scope to identify any arithmetic related vulnerability classes.

    • Fuzz testing. (cargo fuzz)

    • Checking the unsafe code usage. (cargo-geiger)

    • Scanning of Rust files for vulnerabilities.(cargo audit)

    • Deployment to devnet through near-cli

5. RISK METHODOLOGY

Vulnerabilities or issues observed by Halborn are ranked based on the risk assessment methodology by measuring the LIKELIHOOD of a security incident and the IMPACT should an incident occur. This framework works for communicating the characteristics and impacts of technology vulnerabilities. The quantitative model ensures repeatable and accurate measurement while enabling users to see the underlying vulnerability characteristics that were used to generate the Risk scores. For every vulnerability, a risk level will be calculated on a scale of 5 to 1 with 5 being the highest likelihood or impact.
RISK SCALE - LIKELIHOOD
  • 5 - Almost certain an incident will occur.
  • 4 - High probability of an incident occurring.
  • 3 - Potential of a security incident in the long term.
  • 2 - Low probability of an incident occurring.
  • 1 - Very unlikely issue will cause an incident.
RISK SCALE - IMPACT
  • 5 - May cause devastating and unrecoverable impact or loss.
  • 4 - May cause a significant level of impact or loss.
  • 3 - May cause a partial impact or loss to many.
  • 2 - May cause temporary impact or loss.
  • 1 - May cause minimal or un-noticeable impact.
The risk level is then calculated using a sum of these two values, creating a value of 10 to 1 with 10 being the highest level of security risk.
Critical
High
Medium
Low
Informational
  • 10 - CRITICAL
  • 9 - 8 - HIGH
  • 7 - 6 - MEDIUM
  • 5 - 4 - LOW
  • 3 - 1 - VERY LOW AND INFORMATIONAL

6. SCOPE

Out-of-Scope: New features/implementations after the remediation commit IDs.

7. Assessment Summary & Findings Overview

Critical

0

High

0

Medium

6

Low

5

Informational

1

Impact x Likelihood

HAL-01

HAL-02

HAL-03

HAL-05

HAL-06

HAL-04

HAL-07

HAL-08

HAL-09

HAL-11

HAL-12

HAL-10

Security analysisRisk levelRemediation Date
TOKEN PRICE MAINTAINER AS WELL AS RELAYER CAN BE SET TO THE OWNER’S ACCOUNTIDMediumSolved - 03/14/2022
OWNER ACCOUNTID CAN BE SET TO AN INVALID VALUEMediumSolved - 03/14/2022
LACK OF VALIDATION ALLOWS SETTING PERCENTAGES HIGHER THAN A HUNDREDMediumPartially Solved
CASE SENSITIVE CHECK ALLOWS ADDING THE SAME NEAR FUNGIBLE TOKEN MORE THAN ONCEMediumSolved - 03/14/2022
MISSING ZERO CHECKS ON AMOUNTS AND PRICESMediumNot Applicable
LACK OF UPPER LIMIT CHECKS ALLOWS BLOCKING WITHDRAWALSMediumRisk Accepted
MINIMUM VALIDATOR COUNT CAN BE SET TO 0 OR 1LowRisk Accepted
LACK OF UPPER BOUND CHECKS ON DECIMALS LEADS TO PANICSLowRisk Accepted
DEFAULT BRIDGING STATE FOR NEW NEAR FUNGIBLE TOKENS IS `ACTIVE` WHILE IT SHOULD BE `CLOSED`LowSolved - 03/14/2022
NO URL VALIDATION ON SETTING RPC AND SubQL ENDPOINTSLowRisk Accepted
LACK OF UPPER LIMIT CHECKS MAY CAUSE RESOURCE EXHAUSTIONLowRisk Accepted
LACK OF VALIDATOR ACCOUNTID VALIDATION ALLOWS USING INVALID ACCOUNTIDInformational-

8. Findings & Tech Details

8.1 TOKEN PRICE MAINTAINER AS WELL AS RELAYER CAN BE SET TO THE OWNER’S ACCOUNTID

// Medium

Description

The set_token_price_maintainer_account() and set_relayer_account() methods implemented for the AppchainAnchor struct and can be found in appchain-anchor/src/user_actions/settings_manager.rs do not validate the AccountId passed to them to ensure that no matches that of the owner, allowing a malicious owner to bypass the privilege separation point in this case.

Code Location

appchain-anchor/src/user_actions/settings_manager.rs

fn set_token_price_maintainer_account(&mut self, account_id: AccountId) {
    self.assert_owner();
    let mut anchor_settings = self.anchor_settings.get().unwrap();
    anchor_settings.token_price_maintainer_account = account_id;
    self.anchor_settings.set(&anchor_settings);
}

appchain-anchor/src/user_actions/settings_manager.rs

fn set_relayer_account(&mut self, account_id: AccountId) {
    self.assert_owner();
    let mut anchor_settings = self.anchor_settings.get().unwrap();
    anchor_settings.relayer_account = account_id;
    self.anchor_settings.set(&anchor_settings);
}

The following test case was created as a PoC:

fn test_set_owner_as_maintainer(){
    let total_supply = common::to_oct_amount(TOTAL_SUPPLY);
    let (root, _, _registry, anchor, _) = common::init(total_supply, false);
    let result = settings_actions::set_token_price_maintainer_account(&root, &anchor, &root);
    result.assert_success();
    let result2 = view!(anchor.get_owner());
    let owner = result2.unwrap_json::<String>();
    let anchor_settings = anchor_viewer::get_anchor_settings(&anchor);
    let maintainer = anchor_settings.token_price_maintainer_account;
    assert!(owner == maintainer);
}
Score
Impact: 5
Likelihood: 1
Recommendation

SOLVED: The Octopus Network team solved the issue in commit ef2219a37c5be402cec720d9db03501981c2ca80

8.2 OWNER ACCOUNTID CAN BE SET TO AN INVALID VALUE

// Medium

Description

The set_owner() method implemented in the AppchainAnchor Struct, which can be found in “appchain-anchor/src/lib.rs”, does not validate that the AccountId value passed to it actually contains a valid AccountId following the NEAR’s account ID rules. As a result, an owner who wishes to update pass ownership to another user can erroneously call the function with a string pointing to an invalid NEAR account ID, resulting in complete and irreversible loss of control over the contract from that point forward.

Code Location

appchain-anchor/src/lib.rs

fn set_owner(&mut self, owner: AccountId) {
    self.assert_owner();
    self.owner = owner;
}

The following is a test case developed as a PoC, notice that the test prints Owner is: th!$1$!nv@|!d when finished:

fn test_set_invalid_owner(){
    let total_supply = common::to_oct_amount(TOTAL_SUPPLY);
    let (root, _, _registry, anchor, _) = common::init(total_supply, false);
    anchor.contract.set_owner("test".to_string());
    let result1 = call!(root, anchor.set_owner("th!$_1$_!nv@|!d".to_string()));
    result1.assert_success();
    let result = view!(anchor.get_owner());
    println!("New owner is: {}", result.unwrap_json::<String>());
}
Score
Impact: 5
Likelihood: 1
Recommendation

SOLVED: The Octopus Network team solved the issue in commit ef2219a37c5be402cec720d9db03501981c2ca80

8.3 LACK OF VALIDATION ALLOWS SETTING PERCENTAGES HIGHER THAN A HUNDRED

// Medium

Description

The change_maximum_validator_stake_percent() method in \"appchain-anchor/src/user\textunderscore actions/settings_manager.rs\" checks that the percentage value passed to it is less than a 100 and reverts otherwise. However, all the remaining functions allowing the owner to change other percentage values do not perform such checks, allowing percentages to exceed 100%, which would probably cause the contract to crash and panic while rewards are being distributed.

Code Location

appchain-anchor/src/user_actions/settings_manager.rs

fn change_maximum_market_value_percent_of_near_fungible_tokens(&mut self, value: u16) {
    self.assert_owner();
    let mut protocol_settings = self.protocol_settings.get().unwrap();
    assert!(
        value != protocol_settings.maximum_market_value_percent_of_near_fungible_tokens,
        "The value is not changed."
    );
    protocol_settings.maximum_market_value_percent_of_near_fungible_tokens = value;
    self.protocol_settings.set(&protocol_settings);
}

appchain-anchor/src/user_actions/settings_manager.rs

fn change_maximum_market_value_percent_of_wrapped_appchain_token(&mut self, value: u16) {
    self.assert_owner();
    let mut protocol_settings = self.protocol_settings.get().unwrap();
    assert!(
        value != protocol_settings.maximum_market_value_percent_of_wrapped_appchain_token,
        "The value is not changed."
    );
    protocol_settings.maximum_market_value_percent_of_wrapped_appchain_token = value;
    self.protocol_settings.set(&protocol_settings);
}

appchain-anchor/src/user_actions/settings_manager.rs

fn change_validator_commission_percent(&mut self, value: u16) {
    self.assert_owner();
    let mut protocol_settings = self.protocol_settings.get().unwrap();
    assert!(
        value != protocol_settings.maximum_market_value_percent_of_near_fungible_tokens,
        "The value is not changed."
    );
    protocol_settings.maximum_market_value_percent_of_near_fungible_tokens = value;
    self.protocol_settings.set(&protocol_settings);
}
Score
Impact: 5
Likelihood: 1
Recommendation

PARTIALLY SOLVED: The Octopus Network team partially solved the issue in commit ef2219a37c5be402cec720d9db03501981c2ca80

Then some checks were removed in commit eaa2a5109bca0522f6a285f53ebe1e366475bbc6

8.4 CASE SENSITIVE CHECK ALLOWS ADDING THE SAME NEAR FUNGIBLE TOKEN MORE THAN ONCE

// Medium

Description

The register_near_fungible_token() function in \"appchain-anchor/src/assets/near_fungible_tokens.rs\" only checks if the symbol of the token passed to it already exists, however the check is case-sensitive, so it can be bypassed. This allows the owner to register the same token more than once, which can lead to users distributing their funds under different NEAR token contracts instead of one, reducing liquidity and the rewards.

Code Location

appchain-anchor/src/assets/near_fungible_tokens.rs

fn register_near_fungible_token(
    &mut self,
    symbol: String,
    name: String,
    decimals: u8,
    contract_account: AccountId,
    price: U128,
) {
    self.assert_owner();
    let mut near_fungible_tokens = self.near_fungible_tokens.get().unwrap();
    assert!(
        !near_fungible_tokens.contains(&symbol),
        "Token '{}' is already registered.",
        &symbol
    );
    near_fungible_tokens.insert(&NearFungibleToken {
        metadata: FungibleTokenMetadata {
            spec: "ft-1.0.0".to_string(),
            symbol,
            name,
            decimals,
            icon: None,
            reference: None,
            reference_hash: None,
        },
        contract_account,
        price_in_usd: price,
        locked_balance: U128::from(0),
        bridging_state: BridgingState::Active,
    });
    self.near_fungible_tokens.set(&near_fungible_tokens);
}

The following test case reproduces the issue and prints the two tokens that were registered with similar names:

#[test]
fn test_same_token_registeration(){
    let total_supply = common::to_oct_amount(TOTAL_SUPPLY);
    let (root, _, _registry, anchor, _) = common::init(total_supply, false);
    let result = call!(root, anchor.register_near_fungible_token(
        "HLB".to_string(), "Halborn".to_string(),
        2, "test1".to_string(), U128::from(1)
    ));
    result.assert_success();
    let result = call!(root, anchor.register_near_fungible_token(
        "hlb".to_string(), "halborn".to_string(),
        2, "test2".to_string(), U128::from(5)
    ));
    result.assert_success();
    let result2 = view!(anchor.get_near_fungible_tokens());
    let tokens = result2.unwrap_json::<Vec<NearFungibleToken>>();
    tokens.iter().for_each(|token|{
        println!("Token name: {}, symbol: {}", token.metadata.name, token.metadata.symbol);
    });
}
Score
Impact: 3
Likelihood: 3
Recommendation

SOLVED: The Octopus Network team solved the issue in commit ef2219a37c5be402cec720d9db03501981c2ca80

8.5 MISSING ZERO CHECKS ON AMOUNTS AND PRICES

// Medium

Description

Checks should be implemented on amount and price values to make sure they are not set to invalid values, including setting such fields to zero. The set_price_of_oct_token() method implemented in the AppchainAnchor struct in \"appchain-anchor/src/lib.rs\" does not employ such checks to validate that the price of the OCT token does not drop to 0.

Code Location

appchain-anchor/src/lib.rs

pub fn set_price_of_oct_token(&mut self, price: U128) {
    let anchor_settings = self.anchor_settings.get().unwrap();
    assert_eq!(
        env::predecessor_account_id(),
        anchor_settings.token_price_maintainer_account,
        "Only '{}' can call this function.",
        anchor_settings.token_price_maintainer_account
    );
    let mut oct_token = self.oct_token.get().unwrap();
    oct_token.price_in_usd = price;
    self.oct_token.set(&oct_token);
}
Score
Impact: 4
Likelihood: 2
Recommendation

NOT APPLICABLE: The Octopus Network team marked the issue as not applicable, as setting OCT to 0 is needed to remove the cross-chain asset transfer restriction.

8.6 LACK OF UPPER LIMIT CHECKS ALLOWS BLOCKING WITHDRAWALS

// Medium

Description

The change_unlock_period_of_delegator_deposit() and change_unlock_period_of_validator_deposit() functions in \"appchain-anchor/src/user_actions/settings_manager.rs\" do not check for an upper bound for the values passed to them. These functions allow the owner to set the number of days before validators/delegators can withdraw their rewards.

By not checking for an upper bound, the owner can set the values to big numbers that would correspond to years before validators/delegators can actually withdraw their balances.

Code Location

appchain-anchor/src/user_actions/settings_manager.rs

fn change_unlock_period_of_delegator_deposit(&mut self, value: U64) {
    self.assert_owner();
    let mut protocol_settings = self.protocol_settings.get().unwrap();
    assert!(
        value.0 != protocol_settings.unlock_period_of_delegator_deposit.0,
        "The value is not changed."
    );
    protocol_settings.unlock_period_of_delegator_deposit = value;
    self.protocol_settings.set(&protocol_settings);
}

appchain-anchor/src/user_actions/settings_manager.rs

fn change_unlock_period_of_validator_deposit(&mut self, value: U64) {
    self.assert_owner();
    let mut protocol_settings = self.protocol_settings.get().unwrap();
    assert!(
        value.0 != protocol_settings.unlock_period_of_validator_deposit.0,
        "The value is not changed."
    );
    protocol_settings.unlock_period_of_validator_deposit = value;
    self.protocol_settings.set(&protocol_settings);
}
Score
Impact: 4
Likelihood: 2
Recommendation

RISK ACCEPTED: The Octopus Network team accepted the risk of this finding.

8.7 MINIMUM VALIDATOR COUNT CAN BE SET TO 0 OR 1

// Low

Description

The change_minimum_validator_count() method in \"appchain-anchor/src/user_actions/settings_manager.rs\" does not guarantee that the minimum validator count set by the owner is not less than 0 or 1 validators at the same time, that directly impacts the security of the chain. A minimum validators constant should be set and checked in that function to make sure validator counts cannot go below that threshold.

Code Location

appchain-anchor/src/user_actions/settings_manager.rs

fn change_minimum_validator_count(&mut self, value: U64) {
    self.assert_owner();
    let mut protocol_settings = self.protocol_settings.get().unwrap();
    assert!(
        value.0 != protocol_settings.minimum_validator_count.0,
        "The value is not changed."
    );
    protocol_settings.minimum_validator_count = value;
    self.protocol_settings.set(&protocol_settings);
}
Score
Impact: 2
Likelihood: 2
Recommendation

RISK ACCEPTED: The Octopus Network team accepted the risk stating \"The minimum validator count of the protocol settings will only affect the verification process in the go_booting function, it is just a reference value. Other than that, it does not affect any other functions in the contract or the actual actions/status of the application chain. In some special cases, we can set it to 0 or 1, to change the state of the contract for further operations.\"

8.8 LACK OF UPPER BOUND CHECKS ON DECIMALS LEADS TO PANICS

// Low

Description

The get_market_value_of() function in \"appchain-anchor/src/assets/near_fungible_tokens.rs\" and \"appchain-anchor/src/assets/wrapped_appchain_token.rs\" uses the decimals value in the respective token as an exponent while raising a power and the value is then stored in a u128 variable. The issue here is that the decimals value is not validated before being used when the token is created, leading to the ability to pass any value in the range of 0-255, which allows the token creator to cause a panic case whenever the get_market_value_of() function is called since the pow() function call would yield a value well beyond the possible range of u128.

Code Location

appchain-anchor/src/assets/near_fungible_tokens.rs

pub fn get_market_value_of(&self, symbol: &String, amount: u128) -> Balance {
    if let Some(near_fungible_token) = self.tokens.get(&symbol) {
        amount / u128::pow(10, u32::from(near_fungible_token.metadata.decimals))
            * near_fungible_token.price_in_usd.0
    } else {
        0
    }
}

appchain-anchor/src/assets/wrapped_appchain_token.rs

pub fn get_market_value_of(&self, amount: u128) -> Balance {
    amount / u128::pow(10, u32::from(self.metadata.decimals)) * self.price_in_usd.0
}
Score
Impact: 2
Likelihood: 2
Recommendation

RISK ACCEPTED: The Octopus Network team accepted the risk of this finding.

8.9 DEFAULT BRIDGING STATE FOR NEW NEAR FUNGIBLE TOKENS IS `ACTIVE` WHILE IT SHOULD BE `CLOSED`

// Low

Description

The implementation detail document states that newly registered NEAR fungible tokens should have their bridging state set to Closed. However, the register_near_fungible_token() function in \"appchain-anchor/src/assets/near_fungible_tokens.rs\" registers the token with the bridging state of Active.

Code Location

appchain-anchor/src/assets/near_fungible_tokens.rs

fn register_near_fungible_token(
    &mut self,
    symbol: String,
    name: String,
    decimals: u8,
    contract_account: AccountId,
    price: U128,
) {
    self.assert_owner();
    let mut near_fungible_tokens = self.near_fungible_tokens.get().unwrap();
    assert!(
        !near_fungible_tokens.contains(&symbol),
        "Token '{}' is already registered.",
        &symbol
    );
    near_fungible_tokens.insert(&NearFungibleToken {
        metadata: FungibleTokenMetadata {
            spec: "ft-1.0.0".to_string(),
            symbol,
            name,
            decimals,
            icon: None,
            reference: None,
            reference_hash: None,
        },
        contract_account,
        price_in_usd: price,
        locked_balance: U128::from(0),
        bridging_state: BridgingState::Active,
    });
    self.near_fungible_tokens.set(&near_fungible_tokens);
}
Score
Impact: 2
Likelihood: 2
Recommendation

SOLVED: The Octopus Network team solved the issue in commit ef2219a37c5be402cec720d9db03501981c2ca80

8.10 NO URL VALIDATION ON SETTING RPC AND SubQL ENDPOINTS

// Low

Description

The set_rpc_endpoint() and set_subql_endpoint() functions in \"appchain-anchor/src/user_actions/settings_manager.rs\" do not validate the values passed before setting them, allowing the owner to set the URLs to any values that may not follow correct URL forms.

Code Location

appchain-anchor/src/user_actions/settings_manager.rs

fn set_rpc_endpoint(&mut self, rpc_endpoint: String) {
    self.assert_owner();
    let mut appchain_settings = self.appchain_settings.get().unwrap();
    appchain_settings.rpc_endpoint = rpc_endpoint;
    self.appchain_settings.set(&appchain_settings);
}
//
fn set_subql_endpoint(&mut self, subql_endpoint: String) {
    self.assert_owner();
    let mut appchain_settings = self.appchain_settings.get().unwrap();
    appchain_settings.subql_endpoint = subql_endpoint;
    self.appchain_settings.set(&appchain_settings);
}
Score
Impact: 2
Likelihood: 2
Recommendation

RISK ACCEPTED: The Octopus Network team accepted the risk of this finding.

8.11 LACK OF UPPER LIMIT CHECKS MAY CAUSE RESOURCE EXHAUSTION

// Low

Description

The change_maximum_era_count_of_unwithdrawn_reward() function in \"appchain-anchor/src/user_actions/settings_manager.rs\" doesn’t check for an upper limit to the passed value before setting it, allowing the owner to pass a big enough value that is always higher than the end_era value used subsequently in withdraw_delegator_rewards() and withdraw_validator_rewards() (residing in \"appchain-anchor/src/user_actions/staking.rs\") and compared with the maximum_era_count_of_unwithdrawn_reward value. The outcome of this behavior would be looping over a bigger space each time each of these functions is called, which would consume more gas.

Code Location

appchain-anchor/src/user_actions/settings_manager.rs

fn change_maximum_era_count_of_unwithdrawn_reward(&mut self, value: U64) {
    self.assert_owner();
    let mut protocol_settings = self.protocol_settings.get().unwrap();
    assert!(
        value.0 != protocol_settings.maximum_era_count_of_unwithdrawn_reward.0,
        "The value is not changed."
    );
    protocol_settings.maximum_era_count_of_unwithdrawn_reward = value;
    self.protocol_settings.set(&protocol_settings);
}
Score
Impact: 2
Likelihood: 2
Recommendation

RISK ACCEPTED: The Octopus Network team accepted the risk of this finding.

8.12 LACK OF VALIDATOR ACCOUNTID VALIDATION ALLOWS USING INVALID ACCOUNTID

// Informational

Description
Finding description placeholder
Score
Impact:
Likelihood:

9. Automated Testing

AUTOMATED ANALYSIS

Descriptiona

Halborn used automated security scanners to assist with detection of well-known security issues and vulnerabilities. Among the tools used was cargo audit, a security scanner for vulnerabilities reported to the RustSec Advisory Database. All vulnerabilities published in https://crates.io are stored in a repository named The RustSec Advisory Database. cargo audit is a human-readable version of the advisory database which performs a scanning on Cargo.lock. Security Detections are only in scope. All vulnerabilities shown here were already disclosed in the above report. However, to better assist the developers maintaining this code, the auditors are including the output with the dependencies tree, and this is included in the cargo audit output to better know the dependencies affected by unmaintained and vulnerable crates.

Results

\begin{center} \begin{tabular}{|l|p{2cm}|p{9cm}|} \hline \textbf{ID} & \textbf{package} & \textbf{Short Description} \ \hline \href{https://rustsec.org/advisories/RUSTSEC-2020-0159}{RUSTSEC-2020-0159} & chrono & Potential segfault in localtime\textunderscore r invocations \ \hline \href{https://rustsec.org/advisories/RUSTSEC-2021-0067}{RUSTSEC-2021-0067} & cranelift-codegen & Memory access due to code generation flaw in Cranelift module\ \hline \href{https://rustsec.org/advisories/RUSTSEC-2021-0013}{RUSTSEC-2021-0013} & raw-cpuid & Soundness issues in raw-cpuid \ \hline \href{https://rustsec.org/advisories/RUSTSEC-2021-0089}{RUSTSEC-2021-0089} & raw-cpuid & Optional Deserialize implementations lacking validation \ \hline \href{https://rustsec.org/advisories/RUSTSEC-2022-0013}{RUSTSEC-2022-0013} & regex & Regexes with large repetitions on empty sub-expressions take a very long time to parse \ \hline \href{https://rustsec.org/advisories/RUSTSEC-2020-0071}{RUSTSEC-2020-0071} & time & Potential segfault in the time crate \ \hline \href{https://rustsec.org/advisories/RUSTSEC-2021-0110}{RUSTSEC-2021-0110} & wasmtime & Multiple Vulnerabilities in Wasmtime \ \hline \end{tabular} \end{center}

Halborn strongly recommends conducting a follow-up assessment of the project either within six months or immediately following any material changes to the codebase, whichever comes first. This approach is crucial for maintaining the project’s integrity and addressing potential vulnerabilities introduced by code modifications.