-
Notifications
You must be signed in to change notification settings - Fork 0
compiling cario code #189
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
efrat-starkware
wants to merge
1
commit into
main
Choose a base branch
from
efrat/compile_cairo_script
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
compiling cario code #189
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| [package] | ||
| name = "cairo-compile-utils" | ||
| version = "0.1.0" | ||
| edition = "2021" | ||
| description = "The source (Cairo) code of the dummy concat aggregator." | ||
|
|
||
| [features] | ||
| dump_source_files = [] | ||
|
|
||
| [dependencies] | ||
| bincode.workspace = true | ||
| cairo-vm.workspace = true | ||
| cairo-lang-executable.workspace = true | ||
| cairo-lang-runner.workspace = true | ||
| cairo-lang-casm.workspace = true | ||
| cairo-lang-execute-utils.workspace = true | ||
| clap.workspace = true | ||
| num-traits.workspace = true | ||
| serde = { workspace = true, features = ["derive"] } | ||
| serde_json.workspace = true | ||
| thiserror.workspace = true | ||
| thiserror-no-std.workspace = true | ||
| regex.workspace = true | ||
| num-bigint.workspace = true | ||
| walkdir = "2" | ||
| anyhow = "1.0" | ||
|
|
||
| [build-dependencies] | ||
| serde_json.workspace = true | ||
|
|
||
| [dev-dependencies] | ||
| assert_matches = "1.5.0" | ||
| rstest = "0.19.0" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,116 @@ | ||
| use std::process::Command; | ||
| use std::io::{self, Write}; | ||
| use std::path::{Path, PathBuf}; | ||
| use walkdir::WalkDir; // A useful crate for walking directories | ||
| use std::env; // Import the env module | ||
| use anyhow::{Result, Context}; | ||
|
|
||
| use std::fs; | ||
|
|
||
| fn find_main_file(project_root: &PathBuf) -> anyhow::Result<PathBuf> { | ||
| for entry in WalkDir::new(project_root) | ||
| .into_iter() | ||
| .filter_map(Result::ok) | ||
| .filter(|e| e.file_type().is_file() && e.path().extension().map_or(false, |ext| ext == "cairo")) | ||
| { | ||
| let content = fs::read_to_string(entry.path())?; | ||
| if content.contains("func main") { | ||
| return Ok(entry.path().to_path_buf()); | ||
| } | ||
| } | ||
|
|
||
| Err(anyhow::anyhow!( | ||
| "No Cairo 0 file with `func main` found under {:?}", | ||
| project_root | ||
| )) | ||
| } | ||
|
|
||
| /// Builds a shell-like string representation of a Command for logging/debugging. | ||
| /// This is a simplified representation and might not handle all edge cases (e.g., complex quoting). | ||
| fn format_command_for_display(cmd: &Command) -> String { | ||
| let mut cmd_str = String::new(); | ||
|
|
||
| // Add the program name | ||
| if let Some(program) = cmd.get_program().to_str() { | ||
| cmd_str.push_str(program); | ||
| } | ||
|
|
||
| // Add arguments | ||
| for arg in cmd.get_args() { | ||
| if let Some(s) = arg.to_str() { | ||
| cmd_str.push(' '); | ||
| // Basic quoting for arguments that might contain spaces | ||
| if s.contains(' ') || s.contains('"') || s.contains('\'') { | ||
| cmd_str.push('"'); | ||
| cmd_str.push_str(&s.replace('"', "\\\"")); // Escape existing double quotes | ||
| cmd_str.push('"'); | ||
| } else { | ||
| cmd_str.push_str(s); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| cmd_str | ||
| } | ||
|
|
||
| // current_dir should hold the relevant cairo code (only one main) | ||
| pub fn compile_cairo_code(current_dir: &PathBuf) -> anyhow::Result<()> { | ||
|
|
||
| println!("Current working directory: {:?}", current_dir); | ||
|
|
||
| // Define the output path for the compiled program (CASM) | ||
| let output_casm_file = PathBuf::from("./target/my_combined_program.json"); | ||
|
|
||
| // Ensure the target directory exists | ||
| std::fs::create_dir_all(output_casm_file.parent().unwrap())?; | ||
|
|
||
| println!("Searching for Cairo 0 files in: {:?}", current_dir); | ||
|
|
||
| // Define the main Cairo file to compile | ||
| let main_cairo_file = find_main_file(¤t_dir)?; | ||
| println!("Detected main Cairo file: {:?}", main_cairo_file); | ||
|
|
||
| // Check that it exists | ||
| if !main_cairo_file.exists() { | ||
| return Err(anyhow::anyhow!("Main Cairo file not found: {:?}", main_cairo_file)); | ||
| } | ||
|
|
||
| println!("Attempting to compile Cairo 0 project to: {:?}", output_casm_file); | ||
|
|
||
| // Build the `cairo-compile` command | ||
| let mut command = Command::new("cairo-compile"); | ||
|
|
||
| // Add only the main file | ||
| command.arg(&main_cairo_file); | ||
|
|
||
| println!("Executing the following command: {}", format_command_for_display(&command)); | ||
| // Specify the output file | ||
| command.arg("--output").arg(&output_casm_file); | ||
|
|
||
| let cairo_project_root = current_dir.clone(); | ||
| // Add the --cairo_path argument using the determined project root | ||
| // This is crucial for resolving 'from starkware.cairo...' imports if 'starkware' | ||
| // is a top-level directory directly under your project root. | ||
| command.arg("--cairo_path").arg(&cairo_project_root); | ||
|
|
||
| // Execute the command | ||
| let output = command.output()?; | ||
|
|
||
| // Check if the command was successful | ||
| if output.status.success() { | ||
| println!("Cairo 0 compilation successful!"); | ||
| println!("Compiled program written to: {:?}", output_casm_file); | ||
| io::stdout().write_all(&output.stdout)?; | ||
| } else { | ||
| eprintln!("Cairo 0 compilation failed!"); | ||
| eprintln!("Status: {:?}", output.status); | ||
| eprintln!("Stderr:\n{}", String::from_utf8_lossy(&output.stderr)); | ||
| return Err(anyhow::anyhow!("Cairo 0 compilation failed: {}", String::from_utf8_lossy(&output.stderr))); | ||
| } | ||
|
|
||
| println!("\nTo run the compiled Cairo 0 program, you would typically use `cairo-run`:"); | ||
| println!("`cairo-run --program {}`", output_casm_file.display()); | ||
| println!("If `main` is an external function for a StarkNet contract, you'd deploy it."); | ||
|
|
||
| Ok(()) | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| [package] | ||
| name = "concat-aggregator" | ||
| version = "0.1.0" | ||
| edition = "2021" | ||
| description = "The source (Cairo) code of the dummy concat aggregator." | ||
|
|
||
| [features] | ||
| dump_source_files = [] | ||
|
|
||
| [dependencies] | ||
| bincode.workspace = true | ||
| cairo-vm.workspace = true | ||
| cairo-lang-executable.workspace = true | ||
| cairo-lang-runner.workspace = true | ||
| cairo-lang-casm.workspace = true | ||
| cairo-lang-execute-utils.workspace = true | ||
| clap.workspace = true | ||
| num-traits.workspace = true | ||
| serde = { workspace = true, features = ["derive"] } | ||
| serde_json.workspace = true | ||
| thiserror.workspace = true | ||
| thiserror-no-std.workspace = true | ||
| regex.workspace = true | ||
| num-bigint.workspace = true | ||
| walkdir = "2" | ||
| anyhow = "1.0" | ||
| cairo-compile-utils = { path = "../cairo-compile-utils"} | ||
|
|
||
| [build-dependencies] | ||
| serde_json.workspace = true | ||
|
|
||
| [dev-dependencies] | ||
| assert_matches = "1.5.0" | ||
| rstest = "0.19.0" |
Empty file.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,60 @@ | ||
| // Parses the task outputs from the bootloader output. Writes their outputs to the output_ptr. | ||
| // Returns the number of tasks. | ||
| func parse_tasks{output_ptr: felt*}() -> felt { | ||
| alloc_locals; | ||
|
|
||
| local n_tasks: felt; | ||
|
|
||
| %{ | ||
| def parse_bootloader_tasks_outputs(output): | ||
| """ | ||
| Parses the output of the bootloader, returning the raw outputs of the tasks. | ||
| """ | ||
| output_iter = iter(output) | ||
| # Skip the bootloader_config. | ||
| [next(output_iter) for _ in range(3)] | ||
|
|
||
| n_tasks = next(output_iter) | ||
| tasks_outputs = [] | ||
| for _ in range(n_tasks): | ||
| task_output_size = next(output_iter) | ||
| tasks_outputs.append([next(output_iter) for _ in range(task_output_size - 1)]) | ||
|
|
||
| assert next(output_iter, None) is None, "Bootloader output wasn't fully consumed." | ||
|
|
||
| return tasks_outputs | ||
|
|
||
| tasks_outputs = parse_bootloader_tasks_outputs(program_input["bootloader_output"]) | ||
| assert len(tasks_outputs) > 0, "No tasks found in the bootloader output." | ||
| ids.n_tasks = len(tasks_outputs) | ||
| %} | ||
|
|
||
| assert [output_ptr] = n_tasks; | ||
| let output_ptr = output_ptr + 1; | ||
|
|
||
| // Output the task outputs as they are. | ||
| output_tasks(n_tasks=n_tasks); | ||
|
|
||
| return n_tasks; | ||
| } | ||
|
|
||
| // Outputs the task outputs, each with the size of the output (to match the bootloader output | ||
| // format). | ||
| func output_tasks{output_ptr: felt*}(n_tasks: felt) { | ||
| if (n_tasks == 0) { | ||
| return (); | ||
| } | ||
|
|
||
| let output_size = output_ptr[0]; | ||
| let output_ptr = output_ptr + 1; | ||
|
|
||
| %{ | ||
| task_index = len(tasks_outputs) - ids.n_tasks | ||
| segments.load_data(ptr=ids.output_ptr, data=tasks_outputs[task_index]) | ||
| ids.output_size = len(tasks_outputs[task_index]) + 1 | ||
| %} | ||
|
|
||
| let output_ptr = output_ptr + output_size - 1; | ||
|
|
||
| return output_tasks(n_tasks=n_tasks - 1); | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,69 @@ | ||
| %builtins output range_check poseidon | ||
|
|
||
| from aggregator_tasks_utils import parse_tasks | ||
| from starkware.cairo.common.cairo_builtins import PoseidonBuiltin | ||
|
|
||
| // Simple aggregation program that concatenates task outputs. Used for tests. It is not sound. | ||
| // | ||
| // Hint arguments: | ||
| // program_input - List of task outputs, in the format of the bootloader output. | ||
| func main{output_ptr: felt*, range_check_ptr, poseidon_ptr: PoseidonBuiltin*}() { | ||
| alloc_locals; | ||
|
|
||
| let n_tasks = parse_tasks(); | ||
| local output_start: felt* = output_ptr; | ||
|
|
||
| // Output the concatenated task outputs. | ||
| output_concatenated_output(n_tasks=n_tasks); | ||
|
|
||
| %{ | ||
| from starkware.python.math_utils import div_ceil | ||
|
|
||
| output_length = ids.output_ptr - ids.output_start | ||
| page_size = 10 | ||
| next_page_start = min(ids.output_start + page_size, ids.output_ptr) | ||
| next_page_id = 1 | ||
| while next_page_start < ids.output_ptr: | ||
| output_builtin.add_page( | ||
| page_id=next_page_id, | ||
| page_start=next_page_start, | ||
| page_size=min(ids.output_ptr - next_page_start, page_size), | ||
| ) | ||
| next_page_start += page_size | ||
| next_page_id += 1 | ||
| if next_page_id == 1: | ||
| # Single page. Use trivial fact topology. | ||
| output_builtin.add_attribute('gps_fact_topology', [ | ||
| 1, | ||
| 0, | ||
| ]) | ||
| else: | ||
| output_builtin.add_attribute('gps_fact_topology', [ | ||
| next_page_id, | ||
| next_page_id - 1, | ||
| 0, | ||
| 2, | ||
| ]) | ||
| %} | ||
| return (); | ||
| } | ||
|
|
||
| // Outputs the task outputs, without the output sizes. | ||
| func output_concatenated_output{output_ptr: felt*}(n_tasks: felt) { | ||
| alloc_locals; | ||
| if (n_tasks == 0) { | ||
| return (); | ||
| } | ||
|
|
||
| local output_size: felt; | ||
|
|
||
| %{ | ||
| task_index = len(tasks_outputs) - ids.n_tasks | ||
| segments.load_data(ptr=ids.output_ptr, data=tasks_outputs[task_index]) | ||
| ids.output_size = len(tasks_outputs[task_index]) | ||
| %} | ||
|
|
||
| let output_ptr = output_ptr + output_size; | ||
|
|
||
| return output_concatenated_output(n_tasks=n_tasks - 1); | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| use std::process::Command; | ||
| use std::io::{self, Write}; | ||
| use std::path::{Path, PathBuf}; | ||
| use walkdir::WalkDir; // A useful crate for walking directories | ||
| use std::env; // Import the env module | ||
| use anyhow::{Result, Context}; | ||
|
|
||
| use cairo_compile_utils::compile_cairo_code; | ||
| use std::fs; | ||
|
|
||
| fn main() -> anyhow::Result<()> { | ||
|
|
||
| // Get the current working directory of the Rust program | ||
| let current_dir = env::current_dir() | ||
| .context("Failed to get current working directory")?.join("crates").join("concat-aggregator").join("src"); | ||
|
|
||
| compile_cairo_code(¤t_dir)?; | ||
|
|
||
| Ok(()) | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Semgrep identified an issue in your code:
The application builds a file path from potentially untrusted data, which can lead to a path traversal vulnerability. An attacker can manipulate the path which the application uses to access files. If the application does not validate user input and sanitize file paths, sensitive files such as configuration or user data can be accessed, potentially creating or overwriting files. To prevent this vulnerability, validate and sanitize any input that is used to create references to file paths. Also, enforce strict file access controls. For example, choose privileges allowing public-facing applications to access only the required files.
Dataflow graph
flowchart LR classDef invis fill:white, stroke: none classDef default fill:#e7f5ff, color:#1c7fd6, stroke: none subgraph File0["<b>crates/cairo-compile-utils/src/lib.rs</b>"] direction LR %% Source subgraph Source direction LR v0["<a href=https://github.com/starkware-libs/bootloader-hints/blob/98d341a3e5ffab9a225e8524f4f31e02d54e4b3f/crates/cairo-compile-utils/src/lib.rs#L16 target=_blank style='text-decoration:none; color:#1c7fd6'>[Line: 16] entry.path()</a>"] end %% Intermediate %% Sink subgraph Sink direction LR v1["<a href=https://github.com/starkware-libs/bootloader-hints/blob/98d341a3e5ffab9a225e8524f4f31e02d54e4b3f/crates/cairo-compile-utils/src/lib.rs#L16 target=_blank style='text-decoration:none; color:#1c7fd6'>[Line: 16] entry.path()</a>"] end end %% Class Assignment Source:::invis Sink:::invis File0:::invis %% Connections Source --> SinkTo resolve this comment:
✨ Commit Assistant fix suggestion
View step-by-step instructions
entry.path()only points to files within the intended directory (project_root) by checking that the entry's path starts withproject_root. You can do this withentry.path().starts_with(project_root).fs::read_to_string(entry.path()), add a check:if !entry.path().starts_with(project_root) { continue; }.cairofiles in a fixed folder, consider filtering out any files with path components like..or symlinks that could point outside the target directory, to prevent path traversal.This will make sure your code only reads files in your intended directory and avoids unwanted file access.
💬 Ignore this finding
Reply with Semgrep commands to ignore this finding.
/fp <comment>for false positive/ar <comment>for acceptable risk/other <comment>for all other reasonsAlternatively, triage in Semgrep AppSec Platform to ignore the finding created by tainted-path.
You can view more details about this finding in the Semgrep AppSec Platform.