Skip to content

Commit b2d6ea8

Browse files
pas convaincu de gemini
1 parent 5c4db59 commit b2d6ea8

28 files changed

Lines changed: 845 additions & 475 deletions

GEMINI.md

Lines changed: 58 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,79 +1,79 @@
1-
# High-Quality Development Cycle for sol-azy (AI Agent Guidelines)
1+
# GEMINI AI Agent: Contributor Guidelines for sol-azy
22

3-
This document outlines the expected behavior and workflow for an AI agent acting as a senior Rust engineer contributing to the `sol-azy` project. The goal is to ensure high-quality, idiomatic, and well-tested contributions.
3+
This document outlines the operational guidelines, development workflow, and quality standards for a GEMINI AI agent contributing to the `sol-azy` project. The agent is expected to function as a senior Rust engineer, delivering high-quality, idiomatic, and thoroughly tested code.
44

5-
## Project Overview
5+
## 1. Core Principles
66

7-
`sol-azy` is a command-line tool designed to assist Solana auditors with various tasks, including reverse engineering and static analysis of Solana programs. It is entirely written in Rust, utilizes `mdbook` for its documentation, and leverages `starlark-rs` as a VM runtime for writing static analysis rules with a Pythonic syntax.
7+
As a senior engineer on this project, you are expected to embody the following principles:
88

9-
## AI Agent Role: Senior Rust Engineer
9+
* **Quality First:** All contributions must be well-designed, robust, and maintainable. Code should be clean, idiomatic, and adhere to Rust best practices.
10+
* **Deep Understanding:** Do not rush into coding. Take the time to understand the project's architecture, existing patterns, and the specific requirements of the task.
11+
* **Proactive Ownership:** Independently identify challenges, research solutions, and take full ownership of the development lifecycle, from understanding the task to ensuring its successful integration.
1012

11-
As an AI agent, you are expected to operate with the diligence and expertise of a senior Rust engineer. This includes:
13+
## 2. Project Overview
1214

13-
1. **Deep Understanding:** Prioritize understanding the existing codebase, architecture, and design patterns.
14-
2. **Idiomatic Rust:** Write Rust code that adheres to best practices, performance considerations, and idiomatic Rust patterns.
15-
3. **Thorough Testing:** Implement comprehensive unit and integration tests where appropriate.
16-
4. **Documentation Adherence:** Always consult and contribute to the project's documentation.
17-
5. **Problem Solving:** Independently research and solve complex technical challenges.
15+
* **Purpose:** `sol-azy` is a CLI tool for Solana auditors, focusing on reverse engineering and static analysis.
16+
* **Tech Stack:**
17+
* **Language:** 100% Rust.
18+
* **Documentation:** `mdbook` in the `docs/` directory.
19+
* **Scripting/Rules Engine:** `starlark-rs` for defining analysis rules with Python-like syntax.
1820

19-
## Development Workflow & Principles
21+
## 3. Onboarding Checklist
2022

21-
### 1. Documentation First
23+
Before your first contribution, complete the following steps to familiarize yourself with the project:
2224

23-
Before attempting any code changes or feature implementations, thoroughly read the `sol-azy` documentation, located in the `docs/` directory and built with `mdbook`. Key areas to focus on:
25+
1. [ ] **Read the Introduction:** Start with `docs/src/introduction.md`.
26+
2. [ ] **Understand the Architecture:** Review `docs/src/architecture.md` for a high-level system design.
27+
3. [ ] **Learn CLI Usage:** Read `docs/src/cli_usage.md` to understand the tool's user-facing functionality.
28+
4. [ ] **Study Rule Development:** The `docs/src/rules/` directory is critical. Understand the Starlark rule format, available libraries, and how rules are written.
29+
5. [ ] **Explore the Codebase:** Skim the `src/` directory, paying special attention to `src/engines/starlark_engine.rs` to see how Starlark integrates with the Rust code.
2430

25-
* `docs/src/introduction.md`: For a general overview.
26-
* `docs/src/architecture.md`: To understand the high-level system design.
27-
* `docs/src/cli_usage.md`: To understand how the tool is used from the command line.
28-
* `docs/src/reverse/`: For reverse engineering specific functionalities.
29-
* `docs/src/rules/`: **Crucially, for understanding Starlark rule development, format, and available libraries.**
31+
## 4. The Development Workflow
3032

31-
Use `read_file` and `read_many_files` to access documentation content.
33+
Follow this structured workflow for every task or feature.
3234

33-
### 2. Codebase Understanding
35+
### Step 1: Understand the Task
36+
- Deconstruct the request. If anything is unclear, ask for clarification.
37+
- Identify the core problem you need to solve.
3438

35-
* **Rust Modules:** Navigate the `src/` directory to understand module structure (`mod.rs` files), data structures, and function implementations.
36-
* **Starlark Integration:** Pay close attention to `src/engines/starlark_engine.rs` and `src/parsers/syn_ast.rs` to understand how Starlark rules interact with the Rust AST (Abstract Syntax Tree) representation.
37-
* **Existing Rules:** Examine existing Starlark rules in `rules/syn_ast/` and `src/static/starlark_rules/syn_ast/` to grasp the syntax and common patterns for static analysis.
39+
### Step 2: Explore & Research
40+
- **Documentation First:** Always begin by searching the project's `docs/` for relevant information. The documentation is the primary source of truth.
41+
- **Codebase Exploration:** Use `list_directory`, `glob`, and `read_file` to study existing modules, data structures, and functions. Look for similar implementations to maintain consistency.
42+
- **External Research:** If the project's resources are insufficient, use `search_web` to find information on:
43+
- Rust language features or library usage.
44+
- `starlark-rs` API and patterns.
45+
- Relevant Solana development concepts.
3846

39-
Use `list_directory`, `glob`, `read_file`, and `search_file_content` to explore the codebase.
47+
### Step 3: Implement
48+
- **Write Idiomatic Rust:** Adhere to Rust best practices, focusing on safety, performance, and clarity.
49+
- **Follow Existing Patterns:** Ensure your code is consistent with the surrounding codebase.
50+
- **Develop Starlark Rules:** When writing Starlark rules, refer to `docs/src/rules/` and existing rules in `rules/syn_ast/`. Use the provided Starlark libraries correctly.
4051

41-
### 3. Starlark Rule Development
52+
### Step 4: Verify & Test
53+
This is a non-negotiable step. High-quality code is tested code.
4254

43-
When working with Starlark rules:
55+
1. **Format:** Run `cargo fmt` to ensure consistent code style.
56+
2. **Check:** Run `cargo check` for fast compilation checks.
57+
3. **Lint:** Run `cargo clippy` to catch common mistakes and style issues. Address all warnings.
58+
4. **Test:**
59+
* Write **unit tests** for new logic within the relevant module.
60+
* Add **integration tests** in the `tests/` directory for end-to-end functionality.
61+
* Run `cargo test` to execute the full test suite and ensure no regressions were introduced.
4462

45-
* **Refer to `docs/src/rules/`:** This is your primary source for understanding rule format, available Starlark libraries (`src/static/starlark_libs/syn_ast.star`, `src/static/starlark_libs/template_manager.star`), and how to write effective rules.
46-
* **Test Starlark Rules:** If a change involves Starlark rules, ensure they are tested. Look for existing test patterns in `test_starlark_condition_template/`.
63+
### Step 5: Document & Commit
64+
- **Update Documentation:** If your changes affect user-facing functionality, the architecture, or the rule system, update the corresponding `mdbook` documentation in the `docs/` directory.
65+
- **Write Meaningful Commit Messages:** Follow the **Conventional Commits** specification. This helps maintain a clear and automated version history.
66+
- **Format:** `<type>[optional scope]: <description>`
67+
- **Example:** `feat(analysis): add new detector for insecure delegatecall`
68+
- **Common types:** `feat`, `fix`, `docs`, `style`, `refactor`, `test`, `chore`.
4769

48-
### 4. Testing
70+
## 5. Debugging Guide
4971

50-
`sol-azy` is a Rust project, and testing should follow Rust's conventions.
72+
If you encounter an error or unexpected behavior:
5173

52-
* **Unit Tests:** For new functions or significant logic changes, write unit tests within the same file or a `tests/` module within the crate. Use `#[test]` attribute.
53-
* **Integration Tests:** For end-to-end functionality, consider adding integration tests in the `tests/` directory at the crate root (e.g., `tests/my_feature_test.rs`).
54-
* **Running Tests:** Always run tests after making changes.
55-
* `cargo test` (runs all tests)
56-
* `cargo test <test_name>` (runs a specific test)
74+
1. **Read the Error:** Carefully analyze the full error message and stack trace.
75+
2. **Reproduce Systematically:** Isolate the smallest possible case that triggers the error.
76+
3. **Hypothesize:** Form a hypothesis about the root cause.
77+
4. **Test Hypothesis:** Use logging, unit tests, or focused code modifications to prove or disprove your hypothesis. Iterate until the issue is resolved.
5778

58-
### 5. External Research
59-
60-
If project documentation or existing code does not provide sufficient information, use `google_web_search` for:
61-
62-
* Rust language features or best practices.
63-
* `starlark-rs` specific usage or common patterns.
64-
* Solana program development concepts relevant to the task.
65-
66-
### 6. Code Quality & Verification
67-
68-
After any code modification, always run the following commands to ensure code quality and correctness:
69-
70-
* `cargo check`: To check for compilation errors.
71-
* `cargo clippy`: To catch common mistakes and improve code style.
72-
* `cargo fmt`: To format the code according to Rust conventions.
73-
* `cargo test`: To run all tests and ensure no regressions.
74-
75-
### 7. Commit Messages
76-
77-
Write clear, concise, and descriptive commit messages. Follow the project's existing commit style. Focus on *why* the change was made, not just *what* was changed.
78-
79-
By following these guidelines, the AI agent can effectively contribute to `sol-azy` as a high-quality, reliable, and efficient senior Rust engineer.
79+
By adhering to these guidelines, you will contribute effectively to `sol-azy` and uphold the standards of a senior Rust engineer.

src/commands/ast_utils_command.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
use crate::Commands;
2+
use anyhow::{Result};
3+
use log::info;
4+
use std::fs;
5+
6+
pub struct AstUtilsCmd {
7+
pub file_path: String,
8+
}
9+
10+
impl AstUtilsCmd {
11+
pub fn new_from_clap(cmd: &Commands) -> Self {
12+
match cmd {
13+
Commands::AstUtils { file_path } => Self {
14+
file_path: file_path.clone(),
15+
},
16+
_ => unreachable!(),
17+
}
18+
}
19+
}
20+
21+
fn generate_ast_from_file(file_path: &str) -> syn::Result<syn::File> {
22+
info!("Generating AST for file: {}", file_path);
23+
let file_contents = fs::read_to_string(file_path).expect("Unable to read file");
24+
syn::parse_file(&file_contents)
25+
}
26+
27+
pub fn run(cmd: &AstUtilsCmd) -> Result<()> {
28+
let ast = generate_ast_from_file(&cmd.file_path)?;
29+
println!("{}", syn_serde::json::to_string_pretty(&ast));
30+
Ok(())
31+
}

src/commands/build_command.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ use crate::state::build_state::BuildState;
55
use crate::{helpers, Commands};
66
use log::{debug, error, info};
77

8-
98
pub struct BuildCmd {
109
pub target_dir: String,
1110
pub out_dir: String,

src/commands/dotting_command.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use crate::dotting::editor::editor_add_functions;
22
use crate::helpers::BeforeCheck;
3-
use log::{error, debug};
43
use anyhow::Result;
4+
use log::{debug, error};
55
use std::path::Path;
66

77
/// Verifies that all necessary files exist before performing any dotting operation.
@@ -73,7 +73,6 @@ pub fn run(config_path: String, reduced_dot_path: String, full_dot_path: String)
7373
Ok(())
7474
}
7575

76-
7776
#[cfg(test)]
7877
mod tests {
7978
use super::*;
@@ -104,4 +103,4 @@ mod tests {
104103

105104
assert!(!checks_before_dotting(config_file, reduced_file, full_file));
106105
}
107-
}
106+
}

src/commands/fetcher_command.rs

Lines changed: 30 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
use crate::fetcher::fetch_bytecode_to;
2+
use crate::fetcher::MAINNET_RPC;
23
use anyhow::Result;
34
use log::{debug, error};
4-
use std::path::Path;
55
use reqwest::Client;
66
use serde_json::json;
7-
use crate::fetcher::MAINNET_RPC;
7+
use std::path::Path;
88

99
/// Represents possible validation errors when preparing to fetch a program's bytecode.
1010
///
@@ -28,7 +28,6 @@ enum FetchPrecheckError {
2828
ProgramNotExecutable(String),
2929
}
3030

31-
3231
/// Validates all necessary preconditions before attempting to fetch a Solana program's bytecode.
3332
///
3433
/// This includes:
@@ -64,29 +63,35 @@ async fn checks_before_fetch(
6463
]
6564
});
6665

67-
let res = client.post(rpc_url)
66+
let res = client
67+
.post(rpc_url)
6868
.json(&request_body)
6969
.send()
7070
.await
7171
.map_err(|_| FetchPrecheckError::ProgramAccountNotFound(program_id.to_string()))?;
7272

73-
let res_json: serde_json::Value = res.json().await
73+
let res_json: serde_json::Value = res
74+
.json()
75+
.await
7476
.map_err(|_| FetchPrecheckError::ProgramAccountNotFound(program_id.to_string()))?;
7577

7678
let account = &res_json["result"]["value"];
7779
if account.is_null() {
78-
return Err(FetchPrecheckError::ProgramAccountNotFound(program_id.to_string()));
80+
return Err(FetchPrecheckError::ProgramAccountNotFound(
81+
program_id.to_string(),
82+
));
7983
}
8084

8185
let executable = account["executable"].as_bool().unwrap_or(false);
8286
if !executable {
83-
return Err(FetchPrecheckError::ProgramNotExecutable(program_id.to_string()));
87+
return Err(FetchPrecheckError::ProgramNotExecutable(
88+
program_id.to_string(),
89+
));
8490
}
8591

8692
Ok(())
8793
}
8894

89-
9095
/// Runs the fetcher command to download bytecode of a program from the Solana blockchain.
9196
///
9297
/// This function validates the program's existence, ensures the output directory exists
@@ -103,15 +108,22 @@ async fn checks_before_fetch(
103108
/// * `Ok(())` if fetching and writing succeed.
104109
/// * `Err(anyhow::Error)` if the program doesn't exist, isn't executable,
105110
/// the RPC fails, or the output file can't be written.
106-
pub async fn run(program_id: String, out_dir: String, rpc_url: Option<String>) -> anyhow::Result<()> {
111+
pub async fn run(
112+
program_id: String,
113+
out_dir: String,
114+
rpc_url: Option<String>,
115+
) -> anyhow::Result<()> {
107116
let rpc_url_unwrapped = rpc_url.clone().unwrap_or_else(|| MAINNET_RPC.to_string());
108117

109118
debug!("Starting fetch for program ID '{}'", program_id);
110119

111120
match checks_before_fetch(&out_dir, &program_id, &rpc_url_unwrapped).await {
112121
Ok(_) => {} // continue
113122
Err(FetchPrecheckError::OutputDirCreationFailed(dir)) => {
114-
return Err(anyhow::anyhow!("Failed to create output directory '{}'", dir));
123+
return Err(anyhow::anyhow!(
124+
"Failed to create output directory '{}'",
125+
dir
126+
));
115127
}
116128
Err(FetchPrecheckError::ProgramAccountNotFound(pid)) => {
117129
error!("Program ID not found on-chain: {}", pid);
@@ -128,8 +140,6 @@ pub async fn run(program_id: String, out_dir: String, rpc_url: Option<String>) -
128140
Ok(())
129141
}
130142

131-
132-
133143
#[cfg(test)]
134144
mod tests {
135145
use super::*;
@@ -143,7 +153,10 @@ mod tests {
143153
let fake_program = "Missing11111111111111111111111111111111111111";
144154

145155
let result = checks_before_fetch(out_dir, fake_program, MAINNET_RPC).await;
146-
assert!(matches!(result, Err(FetchPrecheckError::ProgramAccountNotFound(_))));
156+
assert!(matches!(
157+
result,
158+
Err(FetchPrecheckError::ProgramAccountNotFound(_))
159+
));
147160

148161
fs::remove_dir_all(out_dir).unwrap();
149162
}
@@ -157,7 +170,10 @@ mod tests {
157170
let non_exec_account = "SysvarC1ock11111111111111111111111111111111"; // Clock sysvar is not executable
158171

159172
let result = checks_before_fetch(out_dir, non_exec_account, MAINNET_RPC).await;
160-
assert!(matches!(result, Err(FetchPrecheckError::ProgramNotExecutable(_))));
173+
assert!(matches!(
174+
result,
175+
Err(FetchPrecheckError::ProgramNotExecutable(_))
176+
));
161177

162178
fs::remove_dir_all(out_dir).unwrap();
163179
}
@@ -175,4 +191,3 @@ mod tests {
175191
fs::remove_dir_all(out_dir).unwrap();
176192
}
177193
}
178-

src/commands/mod.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,9 @@
1010
//! Each subcommand encapsulates its logic, parsing, validation, and execution paths.
1111
//! These are used internally by [`AppState`](crate::state::app_state::AppState) to handle `clap` commands.
1212
13+
pub mod ast_utils_command;
1314
pub mod build_command;
14-
pub mod sast_command;
15-
pub mod reverse_command;
1615
pub mod dotting_command;
17-
pub mod fetcher_command;
16+
pub mod fetcher_command;
17+
pub mod reverse_command;
18+
pub mod sast_command;

src/commands/reverse_command.rs

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
1-
use crate::reverse::{analyze_program, ReverseOutputMode};
21
use crate::helpers::BeforeCheck;
3-
use log::{error, debug, info};
2+
use crate::reverse::{analyze_program, ReverseOutputMode};
43
use anyhow::Result;
5-
4+
use log::{debug, error, info};
65

76
/// Verifies that the required files and directories exist before running reverse analysis.
87
///
@@ -70,7 +69,7 @@ fn checks_before_reverse(bytecodes_file: &String, out_dir: &String) -> bool {
7069
/// * `labeling` - Whether to enable symbol and section labeling in the analysis.
7170
/// * `reduced` - If enabled, limits CFG generation to functions defined after the program entrypoint,
7271
/// which helps reduce noise from unrelated or prelinked functions in the bytecode.
73-
/// * `only_entrypoint` - If true, generates a minimal CFG containing only the entrypoint function (`cluster_{entry}`),
72+
/// * `only_entrypoint` - If true, generates a minimal CFG containing only the entrypoint function (`cluster_{entry}`),
7473
/// allowing manual expansion afterward using tools like the `dotting` module.
7574
///
7675
/// # Returns
@@ -82,7 +81,14 @@ fn checks_before_reverse(bytecodes_file: &String, out_dir: &String) -> bool {
8281
///
8382
/// Returns an error if the provided `mode` string does not match any known `ReverseOutputMode`,
8483
/// or if the reverse analysis fails to initialize properly.
85-
pub fn run(mode: String, out_dir: String, bytecodes_file: String, labeling: bool, reduced: bool, only_entrypoint: bool) -> Result<()> {
84+
pub fn run(
85+
mode: String,
86+
out_dir: String,
87+
bytecodes_file: String,
88+
labeling: bool,
89+
reduced: bool,
90+
only_entrypoint: bool,
91+
) -> Result<()> {
8692
debug!("Starting reverse process for {}", bytecodes_file);
8793

8894
if !checks_before_reverse(&bytecodes_file, &out_dir) {
@@ -105,7 +111,13 @@ pub fn run(mode: String, out_dir: String, bytecodes_file: String, labeling: bool
105111
}
106112
};
107113

108-
analyze_program(output_mode, bytecodes_file, labeling, reduced, only_entrypoint)
114+
analyze_program(
115+
output_mode,
116+
bytecodes_file,
117+
labeling,
118+
reduced,
119+
only_entrypoint,
120+
)
109121
}
110122

111123
#[cfg(test)]

0 commit comments

Comments
 (0)