diff --git a/.github/workflows/gitleaks.yml b/.github/workflows/gitleaks.yml new file mode 100644 index 0000000..3b65940 --- /dev/null +++ b/.github/workflows/gitleaks.yml @@ -0,0 +1,18 @@ +name: gitleaks + +on: + push: + branches: + - "**" + pull_request: + +jobs: + gitleaks: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - uses: gacts/gitleaks@v1 + with: + config-path: .gitleaks.toml diff --git a/.gitignore b/.gitignore index 0592392..26d1baf 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,19 @@ /target .DS_Store +.env +.env.* +!.env.example +.envrc +.direnv/ +.npmrc +.aws/ +.venv/ +venv/ +.cargo/credentials +*.pem +*.key +*.p12 +*.crt +*.cer +*.secret +*.secrets diff --git a/.gitleaks.toml b/.gitleaks.toml new file mode 100644 index 0000000..32f7132 --- /dev/null +++ b/.gitleaks.toml @@ -0,0 +1,7 @@ +title = "aelf-web3.rust gitleaks config" + +[allowlist] +description = "Public readonly test key used by docs, examples, and smoke tests. Never fund it." +regexes = [ + '''(?m)\b0000000000000000000000000000000000000000000000000000000000000001\b''', +] diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..dda86f4 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 AElf + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 61d71e6..7c95ab0 100644 --- a/README.md +++ b/README.md @@ -95,6 +95,8 @@ fn main() -> Result<(), Box> { ## Raw Transaction +The sample private key below is a public test-only readonly key. Never fund it. + ```rust use aelf_sdk::proto::token::TransferInput; use aelf_sdk::{AElfClient, ClientConfig, Wallet, decode_address}; @@ -103,6 +105,7 @@ use prost::Message; #[tokio::main] async fn main() -> Result<(), Box> { let client = AElfClient::new(ClientConfig::new("http://127.0.0.1:8000"))?; + // Public test-only readonly key. Never fund it. let wallet = Wallet::from_private_key( "0000000000000000000000000000000000000000000000000000000000000001", )?; @@ -138,6 +141,8 @@ Public-node note: ## Typed Contracts +The sample private key below is a public test-only readonly key. Never fund it. + ```rust use aelf_sdk::proto::token::GetBalanceInput; use aelf_sdk::{AElfClient, ClientConfig, Wallet, address_to_pb}; @@ -145,6 +150,7 @@ use aelf_sdk::{AElfClient, ClientConfig, Wallet, address_to_pb}; #[tokio::main] async fn main() -> Result<(), Box> { let client = AElfClient::new(ClientConfig::new("http://127.0.0.1:8000"))?; + // Public test-only readonly key. Never fund it. let wallet = Wallet::from_private_key( "0000000000000000000000000000000000000000000000000000000000000001", )?; @@ -164,6 +170,8 @@ async fn main() -> Result<(), Box> { ## Dynamic Contracts +The sample private key below is a public test-only readonly key. Never fund it. + ```rust use aelf_sdk::{AElfClient, ClientConfig, Wallet}; use serde_json::json; @@ -171,6 +179,7 @@ use serde_json::json; #[tokio::main] async fn main() -> Result<(), Box> { let client = AElfClient::new(ClientConfig::new("http://127.0.0.1:8000"))?; + // Public test-only readonly key. Never fund it. let wallet = Wallet::from_private_key( "0000000000000000000000000000000000000000000000000000000000000001", )?; @@ -214,6 +223,8 @@ Useful environment variables: - `AELF_AMOUNT` - `AELF_SEND` +`public_balance` and `dynamic_contract_get_balance` fall back to a public test-only readonly key when `AELF_PRIVATE_KEY` is omitted. Never fund it. + ## Feature Flags v0.1 alpha exposes one transport feature: @@ -430,6 +441,10 @@ MSRV: - The workspace MSRV is Rust `1.85`. - CI enforces it with `cargo +1.85.0 check --workspace --all-targets --all-features --locked`. +## Security + +See [SECURITY.md](SECURITY.md) for private vulnerability disclosure instructions. + ## License MIT diff --git a/README.zh.md b/README.zh.md index 924a2e5..45b4bda 100644 --- a/README.zh.md +++ b/README.zh.md @@ -95,6 +95,8 @@ fn main() -> Result<(), Box> { ## Raw Transaction +下面示例里的私钥是公开的只读测试 key,绝对不要充值或承载资产。 + ```rust use aelf_sdk::proto::token::TransferInput; use aelf_sdk::{AElfClient, ClientConfig, Wallet, decode_address}; @@ -103,6 +105,7 @@ use prost::Message; #[tokio::main] async fn main() -> Result<(), Box> { let client = AElfClient::new(ClientConfig::new("http://127.0.0.1:8000"))?; + // 公开的只读测试 key,绝对不要充值或承载资产。 let wallet = Wallet::from_private_key( "0000000000000000000000000000000000000000000000000000000000000001", )?; @@ -138,6 +141,8 @@ async fn main() -> Result<(), Box> { ## Typed Contracts +下面示例里的私钥是公开的只读测试 key,绝对不要充值或承载资产。 + ```rust use aelf_sdk::proto::token::GetBalanceInput; use aelf_sdk::{AElfClient, ClientConfig, Wallet, address_to_pb}; @@ -145,6 +150,7 @@ use aelf_sdk::{AElfClient, ClientConfig, Wallet, address_to_pb}; #[tokio::main] async fn main() -> Result<(), Box> { let client = AElfClient::new(ClientConfig::new("http://127.0.0.1:8000"))?; + // 公开的只读测试 key,绝对不要充值或承载资产。 let wallet = Wallet::from_private_key( "0000000000000000000000000000000000000000000000000000000000000001", )?; @@ -164,6 +170,8 @@ async fn main() -> Result<(), Box> { ## Dynamic Contracts +下面示例里的私钥是公开的只读测试 key,绝对不要充值或承载资产。 + ```rust use aelf_sdk::{AElfClient, ClientConfig, Wallet}; use serde_json::json; @@ -171,6 +179,7 @@ use serde_json::json; #[tokio::main] async fn main() -> Result<(), Box> { let client = AElfClient::new(ClientConfig::new("http://127.0.0.1:8000"))?; + // 公开的只读测试 key,绝对不要充值或承载资产。 let wallet = Wallet::from_private_key( "0000000000000000000000000000000000000000000000000000000000000001", )?; @@ -214,6 +223,8 @@ cargo run -p aelf-sdk --example raw_transaction_flow - `AELF_AMOUNT` - `AELF_SEND` +如果没有提供 `AELF_PRIVATE_KEY`,`public_balance` 和 `dynamic_contract_get_balance` 会回退到公开的只读测试 key。这个 key 仅用于示例和 smoke test,绝对不要充值或承载资产。 + ## Feature Flags v0.1 alpha 当前有一个传输层 feature: @@ -430,6 +441,10 @@ MSRV 说明: - workspace 的 MSRV 现在是 Rust `1.85`。 - CI 已用 `cargo +1.85.0 check --workspace --all-targets --all-features --locked` 做硬性门禁。 +## 安全 + +私下披露漏洞的方式见 [SECURITY.md](SECURITY.md)。 + ## License MIT diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..9951246 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,20 @@ +# Security Policy + +## Supported Versions + +Security fixes are applied to the latest maintained release line and the current `main` branch. + +## Reporting a Vulnerability + +Please report security vulnerabilities privately to `devops@aelf.io`. + +Do not open a public GitHub issue for undisclosed security problems. + +When reporting a vulnerability, include: + +- affected crate or workflow +- reproduction steps or proof of concept +- impact assessment if known +- any suggested mitigation + +We will acknowledge receipt as soon as practical, investigate privately, and coordinate public disclosure after a fix is available. diff --git a/crates/aelf-client/src/lib.rs b/crates/aelf-client/src/lib.rs index b75dddd..7ad1e9e 100644 --- a/crates/aelf-client/src/lib.rs +++ b/crates/aelf-client/src/lib.rs @@ -40,6 +40,7 @@ use zeroize::Zeroize; const API_BASE: &str = "api/blockChain"; const NET_API_BASE: &str = "api/net"; +// Public test-only readonly key used for system contract lookups. Never fund it. const READONLY_PRIVATE_KEY: &str = "0000000000000000000000000000000000000000000000000000000000000001"; diff --git a/crates/aelf-contract/src/lib.rs b/crates/aelf-contract/src/lib.rs index 4ac0c8b..a8147ee 100644 --- a/crates/aelf-contract/src/lib.rs +++ b/crates/aelf-contract/src/lib.rs @@ -719,6 +719,7 @@ mod tests { Arc, }; + // Public test-only readonly key used by descriptor-related tests. Never fund it. const READONLY_PRIVATE_KEY: &str = "0000000000000000000000000000000000000000000000000000000000000001"; diff --git a/crates/aelf-sdk/examples/dynamic_contract_get_balance.rs b/crates/aelf-sdk/examples/dynamic_contract_get_balance.rs index a60822a..032e211 100644 --- a/crates/aelf-sdk/examples/dynamic_contract_get_balance.rs +++ b/crates/aelf-sdk/examples/dynamic_contract_get_balance.rs @@ -1,15 +1,19 @@ use aelf_sdk::{AElfClient, ClientConfig, Wallet}; use serde_json::json; +// Public test-only readonly key for examples and smoke tests. Never fund it. +const READONLY_PRIVATE_KEY: &str = + "0000000000000000000000000000000000000000000000000000000000000001"; + #[tokio::main(flavor = "current_thread")] async fn main() -> Result<(), Box> { let endpoint = std::env::var("AELF_ENDPOINT").unwrap_or_else(|_| "http://127.0.0.1:8000".to_owned()); let token_address = std::env::var("AELF_TOKEN_CONTRACT")?; let owner = std::env::var("AELF_OWNER_ADDRESS")?; - let private_key = std::env::var("AELF_PRIVATE_KEY").unwrap_or_else(|_| { - "0000000000000000000000000000000000000000000000000000000000000001".to_owned() - }); + // Public test-only readonly key for examples and smoke tests. Never fund it. + let private_key = + std::env::var("AELF_PRIVATE_KEY").unwrap_or_else(|_| READONLY_PRIVATE_KEY.to_owned()); let client = AElfClient::new(ClientConfig::new(endpoint))?; let wallet = Wallet::from_private_key(&private_key)?; diff --git a/crates/aelf-sdk/examples/public_balance.rs b/crates/aelf-sdk/examples/public_balance.rs index 09b97b2..c3708ae 100644 --- a/crates/aelf-sdk/examples/public_balance.rs +++ b/crates/aelf-sdk/examples/public_balance.rs @@ -2,6 +2,7 @@ use aelf_sdk::proto::aelf::Address; use aelf_sdk::proto::token::GetBalanceInput; use aelf_sdk::{decode_address, format_token_amount, AElfClient, ClientConfig, Wallet}; +// Public test-only readonly key for examples and smoke tests. Never fund it. const READONLY_PRIVATE_KEY: &str = "0000000000000000000000000000000000000000000000000000000000000001"; diff --git a/crates/aelf-sdk/tests/local_node.rs b/crates/aelf-sdk/tests/local_node.rs index 28cbe67..c2de727 100644 --- a/crates/aelf-sdk/tests/local_node.rs +++ b/crates/aelf-sdk/tests/local_node.rs @@ -11,6 +11,7 @@ use std::env; use std::error::Error; use tokio::time::{sleep, Duration}; +// Public test-only readonly key used by readonly local-node helpers. Never fund it. const READONLY_PRIVATE_KEY: &str = "0000000000000000000000000000000000000000000000000000000000000001"; diff --git a/crates/aelf-sdk/tests/public_readonly_smoke.rs b/crates/aelf-sdk/tests/public_readonly_smoke.rs index 7662dad..673fc46 100644 --- a/crates/aelf-sdk/tests/public_readonly_smoke.rs +++ b/crates/aelf-sdk/tests/public_readonly_smoke.rs @@ -3,6 +3,7 @@ use aelf_sdk::{address_to_pb, AElfClient, ClientConfig, Wallet}; use serde_json::json; use std::error::Error; +// Public test-only readonly key used by live smoke tests. Never fund it. const READONLY_PRIVATE_KEY: &str = "0000000000000000000000000000000000000000000000000000000000000001"; const MAIN_CHAIN_ENDPOINT: &str = "https://aelf-public-node.aelf.io";