Skip to content

Commit

Permalink
feat: parse cargo build script commands
Browse files Browse the repository at this point in the history
  • Loading branch information
0xCCF4 committed Jul 24, 2024
1 parent 5264432 commit b589ca9
Show file tree
Hide file tree
Showing 7 changed files with 541 additions and 67 deletions.
1 change: 1 addition & 0 deletions untrusted_value_taint_checker/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ tracing = "0"
serde_json = "1.0.120"
tempfile = "3.10.1"
serde = { version = "1.0.204", features = ["derive"] }
semver = "1.0.23"
#owo-colors = { version = "4", features = ["supports-colors"] }
#cargo_metadata = "0.18.1"
#bpaf = { version = "0.9.12", features = ["bpaf_derive", "autocomplete"] }
Expand Down
17 changes: 11 additions & 6 deletions untrusted_value_taint_checker/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,19 @@ fn main() {

for file in taint_source_files {
let file = file.expect("Cannot read directory entry");
let file = file.path();
if file.extension() != Some("json".as_ref()) {
let file_path = file.path();
if file_path.extension() != Some("json".as_ref()) {
continue;
}
let file = fs::File::open(file).expect("Cannot open file");

let taint_module: TaintModuleJson =
serde_json::from_reader(file).expect("Cannot parse JSON");
let file = fs::File::open(&file_path).expect("Cannot open file");

let taint_module: TaintModuleJson = serde_json::from_reader(file).unwrap_or_else(|e| {
panic!(
"Cannot parse JSON in file {}: {}",
file_path.to_string_lossy(),
e
)
});
taint_modules.push(taint_module);
}

Expand Down
281 changes: 266 additions & 15 deletions untrusted_value_taint_checker/src/analysis/build_plan.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,217 @@
use config::Map;
use serde::de::{self, Error};
use serde::{Deserialize, Deserializer, Serialize};
use std::path::PathBuf;
use std::str::FromStr;

use config::Map;
use serde::{Deserialize, Serialize};
// Copied from https://docs.rs/crate/build-plan/0.1.1/source/src/lib.rs
// License:
// CC0 1.0 Universal
//
// Statement of Purpose
//
// The laws of most jurisdictions throughout the world automatically confer
// exclusive Copyright and Related Rights (defined below) upon the creator and
// subsequent owner(s) (each and all, an "owner") of an original work of
// authorship and/or a database (each, a "Work").
//
// Certain owners wish to permanently relinquish those rights to a Work for the
// purpose of contributing to a commons of creative, cultural and scientific
// works ("Commons") that the public can reliably and without fear of later
// claims of infringement build upon, modify, incorporate in other works, reuse
// and redistribute as freely as possible in any form whatsoever and for any
// purposes, including without limitation commercial purposes. These owners may
// contribute to the Commons to promote the ideal of a free culture and the
// further production of creative, cultural and scientific works, or to gain
// reputation or greater distribution for their Work in part through the use and
// efforts of others.
//
// For these and/or other purposes and motivations, and without any expectation
// of additional consideration or compensation, the person associating CC0 with a
// Work (the "Affirmer"), to the extent that he or she is an owner of Copyright
// and Related Rights in the Work, voluntarily elects to apply CC0 to the Work
// and publicly distribute the Work under its terms, with knowledge of his or her
// Copyright and Related Rights in the Work and the meaning and intended legal
// effect of CC0 on those rights.
//
// 1. Copyright and Related Rights. A Work made available under CC0 may be
// protected by copyright and related or neighboring rights ("Copyright and
// Related Rights"). Copyright and Related Rights include, but are not limited
// to, the following:
//
// i. the right to reproduce, adapt, distribute, perform, display, communicate,
// and translate a Work;
//
// ii. moral rights retained by the original author(s) and/or performer(s);
//
// iii. publicity and privacy rights pertaining to a person's image or likeness
// depicted in a Work;
//
// iv. rights protecting against unfair competition in regards to a Work,
// subject to the limitations in paragraph 4(a), below;
//
// v. rights protecting the extraction, dissemination, use and reuse of data in
// a Work;
//
// vi. database rights (such as those arising under Directive 96/9/EC of the
// European Parliament and of the Council of 11 March 1996 on the legal
// protection of databases, and under any national implementation thereof,
// including any amended or successor version of such directive); and
//
// vii. other similar, equivalent or corresponding rights throughout the world
// based on applicable law or treaty, and any national implementations thereof.
//
// 2. Waiver. To the greatest extent permitted by, but not in contravention of,
// applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and
// unconditionally waives, abandons, and surrenders all of Affirmer's Copyright
// and Related Rights and associated claims and causes of action, whether now
// known or unknown (including existing as well as future claims and causes of
// action), in the Work (i) in all territories worldwide, (ii) for the maximum
// duration provided by applicable law or treaty (including future time
// extensions), (iii) in any current or future medium and for any number of
// copies, and (iv) for any purpose whatsoever, including without limitation
// commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes
// the Waiver for the benefit of each member of the public at large and to the
// detriment of Affirmer's heirs and successors, fully intending that such Waiver
// shall not be subject to revocation, rescission, cancellation, termination, or
// any other legal or equitable action to disrupt the quiet enjoyment of the Work
// by the public as contemplated by Affirmer's express Statement of Purpose.
//
// 3. Public License Fallback. Should any part of the Waiver for any reason be
// judged legally invalid or ineffective under applicable law, then the Waiver
// shall be preserved to the maximum extent permitted taking into account
// Affirmer's express Statement of Purpose. In addition, to the extent the Waiver
// is so judged Affirmer hereby grants to each affected person a royalty-free,
// non transferable, non sublicensable, non exclusive, irrevocable and
// unconditional license to exercise Affirmer's Copyright and Related Rights in
// the Work (i) in all territories worldwide, (ii) for the maximum duration
// provided by applicable law or treaty (including future time extensions), (iii)
// in any current or future medium and for any number of copies, and (iv) for any
// purpose whatsoever, including without limitation commercial, advertising or
// promotional purposes (the "License"). The License shall be deemed effective as
// of the date CC0 was applied by Affirmer to the Work. Should any part of the
// License for any reason be judged legally invalid or ineffective under
// applicable law, such partial invalidity or ineffectiveness shall not
// invalidate the remainder of the License, and in such case Affirmer hereby
// affirms that he or she will not (i) exercise any of his or her remaining
// Copyright and Related Rights in the Work or (ii) assert any associated claims
// and causes of action with respect to the Work, in either case contrary to
// Affirmer's express Statement of Purpose.
//
// 4. Limitations and Disclaimers.
//
// a. No trademark or patent rights held by Affirmer are waived, abandoned,
// surrendered, licensed or otherwise affected by this document.
//
// b. Affirmer offers the Work as-is and makes no representations or warranties
// of any kind concerning the Work, express, implied, statutory or otherwise,
// including without limitation warranties of title, merchantability, fitness
// for a particular purpose, non infringement, or the absence of latent or
// other defects, accuracy, or the present or absence of errors, whether or not
// discoverable, all to the greatest extent permissible under applicable law.
//
// c. Affirmer disclaims responsibility for clearing rights of other persons
// that may apply to the Work or any use thereof, including without limitation
// any person's Copyright and Related Rights in the Work. Further, Affirmer
// disclaims responsibility for obtaining any necessary consents, permissions
// or other rights required for any use of the Work.
//
// d. Affirmer understands and acknowledges that Creative Commons is not a
// party to this document and has no duty or obligation with respect to this
// CC0 or use of the Work.
//
// For more information, please see
// <http://creativecommons.org/publicdomain/zero/1.0/>

#[derive(Deserialize, Serialize, Debug, Clone)]
pub struct BuildPlan {
pub invocations: Vec<BuildInvocation>,
pub inputs: Vec<PathBuf>,
/// Whether an object is for the host arch, or the target arch.
///
/// These will be the same unless cross-compiling.
#[derive(PartialEq, Eq, Hash, Debug, Clone, Copy, PartialOrd, Ord, Serialize, Deserialize)]
pub enum Kind {
Host,
Target,
}

/// Kinds of libraries that can be created.
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum LibKind {
Lib,
Rlib,
Dylib,
ProcMacro,
Other(String),
}

impl LibKind {
pub fn parse_str(string: &str) -> LibKind {
match string {
"lib" => LibKind::Lib,
"rlib" => LibKind::Rlib,
"dylib" => LibKind::Dylib,
"proc-macro" => LibKind::ProcMacro,
s => LibKind::Other(s.to_string()),
}
}

/// Returns the argument suitable for `--crate-type` to pass to rustc.
pub fn crate_type(&self) -> &str {
match *self {
LibKind::Lib => "lib",
LibKind::Rlib => "rlib",
LibKind::Dylib => "dylib",
LibKind::ProcMacro => "proc-macro",
LibKind::Other(ref s) => s,
}
}

pub fn linkable(&self) -> bool {
match *self {
LibKind::Lib | LibKind::Rlib | LibKind::Dylib | LibKind::ProcMacro => true,
LibKind::Other(..) => false,
}
}
}

/// Describes artifacts that can be produced using `cargo build`.
#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub enum TargetKind {
Lib(Vec<LibKind>),
Bin,
Test,
Bench,
ExampleLib(Vec<LibKind>),
ExampleBin,
CustomBuild,
}

impl TargetKind {
pub fn is_cdylib(&self) -> bool {
match self {
TargetKind::Lib(libtypes) => libtypes.contains(&LibKind::Dylib),
TargetKind::ExampleLib(libtypes) => libtypes.contains(&LibKind::Dylib),
_ => false,
}
}
}

impl<'de> de::Deserialize<'de> for TargetKind {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: de::Deserializer<'de>,
{
use self::TargetKind::*;

let raw = Vec::<&str>::deserialize(deserializer)?;
Ok(match *raw {
[] => return Err(D::Error::invalid_length(0, &"at least one target kind")),
["bin"] => Bin,
["example"] => ExampleBin,
["test"] => Test,
["custom-build"] => CustomBuild,
["bench"] => Bench,
ref lib_kinds => Lib(lib_kinds.iter().cloned().map(LibKind::parse_str).collect()),
})
}
}

#[derive(Deserialize, Serialize, Debug, Clone)]
Expand All @@ -17,17 +222,63 @@ pub enum CompileMode {
Build,
}

#[derive(Deserialize, Serialize, Debug, Clone)]
pub struct BuildInvocation {
fn semver_from_string<'de, D>(deserializer: D) -> Result<semver::Version, D::Error>
where
D: Deserializer<'de>,
{
let s: String = Deserialize::deserialize(deserializer)?;
semver::Version::from_str(&s).map_err(D::Error::custom)
}

/// A tool invocation.
#[derive(Debug, Deserialize)]
pub struct Invocation {
/// The package this invocation is building a part of.
pub package_name: String,
pub package_version: String,
pub target_kind: Vec<String>,
pub kind: Option<String>,
pub compile_mode: CompileMode,
/// Version of the package that is being built.
#[serde(deserialize_with = "semver_from_string")]
pub package_version: semver::Version,
/// The kind of artifact this invocation creates.
pub target_kind: TargetKind,
/// Whether the files created by this invocation are for the host or target system.
pub kind: Option<Kind>,
/// List of invocations this invocation depends on.
///
/// The vector contains indices into the [`BuildPlan::invocations`] list.
///
/// [`BuildPlan::invocations`]: struct.BuildPlan.html#structfield.invocations
pub deps: Vec<usize>,
/// List of output artifacts (binaries/libraries) created by this invocation.
pub outputs: Vec<PathBuf>,
pub links: Map<String, String>,
pub program: PathBuf,
/// Compile mode
pub compile_mode: CompileMode,
/// Hardlinks of output files that should be placed.
pub links: Map<PathBuf, PathBuf>,
/// The program to invoke.
pub program: String,
/// Arguments to pass to the program.
pub args: Vec<String>,
/// Map of environment variables.
pub env: Map<String, String>,
pub cwd: PathBuf,
/// The working directory in which to execute the program.
pub cwd: Option<PathBuf>,
}

/// A build plan output by `cargo build --build-plan`.
#[derive(Debug, Deserialize)]
pub struct BuildPlan {
/// Program invocations needed to build the target (along with dependency information).
pub invocations: Vec<Invocation>,
/// List of Cargo manifests involved in the build.
pub inputs: Vec<PathBuf>,
}

impl BuildPlan {
/// Parses a `BuildPlan` from Cargo's JSON output.
///
/// Build plan output can be obtained by running `cargo build --build-plan`. Generating build
/// plans for individual targets (tests, examples, etc.) also works.
pub fn from_cargo_output<S: AsRef<[u8]>>(output: S) -> serde_json::Result<Self> {
serde_json::from_slice(output.as_ref())
}
}
2 changes: 1 addition & 1 deletion untrusted_value_taint_checker/src/analysis/callbacks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use tracing::{event, span, Level};

pub struct TaintCompilerCallbacks {
pub package_name: String,
pub package_version: String,
pub package_version: semver::Version,
pub internal_interface_functions: Vec<FunctionInfo>,
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::env;
use std::path::PathBuf;

use super::build_plan::BuildInvocation;
use super::build_plan::Invocation;

pub struct InvocationEnvironment {
vars: Vec<(String, String)>,
Expand All @@ -10,7 +10,7 @@ pub struct InvocationEnvironment {
}

impl InvocationEnvironment {
pub fn enter(environment: &BuildInvocation) -> InvocationEnvironment {
pub fn enter(environment: &Invocation) -> InvocationEnvironment {
let mut result = InvocationEnvironment {
vars: Vec::with_capacity(environment.env.len()),
cwd: env::current_dir().unwrap(),
Expand All @@ -21,7 +21,9 @@ impl InvocationEnvironment {
result.vars.push((key.to_owned(), value.to_owned()));
}

env::set_current_dir(&environment.cwd).unwrap();
if let Some(cwd) = &environment.cwd {
env::set_current_dir(cwd).unwrap();
}

for (key, value) in environment.env.iter() {
env::set_var(key, value);
Expand Down
Loading

0 comments on commit b589ca9

Please sign in to comment.