Skip to content

Commit

Permalink
Merge pull request #1766 from greenbone/rs-nasl-unsafe
Browse files Browse the repository at this point in the history
Implement "unsafe" builtins
  • Loading branch information
Tehforsch authored Dec 6, 2024
2 parents 22b614e + 9f85f45 commit 751266a
Show file tree
Hide file tree
Showing 12 changed files with 252 additions and 31 deletions.
12 changes: 7 additions & 5 deletions rust/crates/nasl-function-proc-macro/src/codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ impl<'a> ArgsStruct<'a> {
self.args.iter().any(|arg| {
matches!(
arg.kind,
ArgKind::PositionalIterator | ArgKind::CheckedPositionalIterator
ArgKind::PositionalIterator(_) | ArgKind::CheckedPositionalIterator(_)
)
})
}
Expand Down Expand Up @@ -84,14 +84,16 @@ impl<'a> ArgsStruct<'a> {
_register
}
},
ArgKind::PositionalIterator => {
ArgKind::PositionalIterator(arg) => {
let position = arg.position;
quote! {
crate::nasl::utils::function::Positionals::new(_register)
crate::nasl::utils::function::Positionals::new(_register, #position)
}
}
ArgKind::CheckedPositionalIterator => {
ArgKind::CheckedPositionalIterator(arg) => {
let position = arg.position;
quote! {
crate::nasl::utils::function::CheckedPositionals::new(_register)?
crate::nasl::utils::function::CheckedPositionals::new(_register, #position)?
}
}
};
Expand Down
2 changes: 1 addition & 1 deletion rust/crates/nasl-function-proc-macro/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ impl Error {
"Specific type specified in receiver argument. Currently, only `&self` is supported."
}
ErrorKind::WrongArgumentOrder => {
"Argument in wrong position. Order of arguments should be: Context/Register, Positionals, Named"
"Argument in wrong position. Order of arguments should be: Context/Register, Positionals, Named, *Positional list"
}
}.into()
}
Expand Down
4 changes: 2 additions & 2 deletions rust/crates/nasl-function-proc-macro/src/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,10 @@ impl Attrs {
return ArgKind::Register;
}
if ty_name_is(ty, "Positionals") {
return ArgKind::PositionalIterator;
return ArgKind::PositionalIterator(PositionalsArg { position });
}
if ty_name_is(ty, "CheckedPositionals") {
return ArgKind::CheckedPositionalIterator;
return ArgKind::CheckedPositionalIterator(PositionalsArg { position });
}
let attr_kind = self
.attrs
Expand Down
13 changes: 9 additions & 4 deletions rust/crates/nasl-function-proc-macro/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ pub enum ArgKind {
MaybeNamed(PositionalArg, NamedArg),
Context,
Register,
PositionalIterator,
CheckedPositionalIterator,
PositionalIterator(PositionalsArg),
CheckedPositionalIterator(PositionalsArg),
}

impl ArgKind {
Expand All @@ -69,8 +69,8 @@ impl ArgKind {
ArgKind::Positional(_) => 1,
ArgKind::MaybeNamed(_, _) => 2,
ArgKind::Named(_) => 3,
ArgKind::PositionalIterator => 4,
ArgKind::CheckedPositionalIterator => 4,
ArgKind::PositionalIterator(_) => 4,
ArgKind::CheckedPositionalIterator(_) => 4,
}
}
}
Expand All @@ -82,3 +82,8 @@ pub struct NamedArg {
pub struct PositionalArg {
pub position: usize,
}

pub struct PositionalsArg {
/// The position of the first argument in the iterator
pub position: usize,
}
4 changes: 4 additions & 0 deletions rust/src/nasl/builtin/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use super::http::HttpError;
use super::isotime::IsotimeError;
use super::knowledge_base::KBError;
use super::regex::RegexError;
use super::sys::SysError;
use super::{misc::MiscError, network::socket::SocketError, ssh::SshError, string::StringError};

#[derive(Debug, Error)]
Expand All @@ -36,6 +37,8 @@ pub enum BuiltinError {
Host(HostError),
#[error("{0}")]
Cert(CertError),
#[error("{0}")]
Sys(SysError),
#[cfg(feature = "nasl-builtin-raw-ip")]
#[error("{0}")]
RawIp(super::raw_ip::RawIpError),
Expand Down Expand Up @@ -81,6 +84,7 @@ builtin_error_variant!(RegexError, Regex);
builtin_error_variant!(KBError, KB);
builtin_error_variant!(HostError, Host);
builtin_error_variant!(CertError, Cert);
builtin_error_variant!(SysError, Sys);

#[cfg(feature = "nasl-builtin-raw-ip")]
builtin_error_variant!(super::raw_ip::RawIpError, RawIp);
2 changes: 2 additions & 0 deletions rust/src/nasl/builtin/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ mod regex;
mod report_functions;
mod ssh;
mod string;
mod sys;

#[cfg(test)]
mod tests;
Expand Down Expand Up @@ -57,6 +58,7 @@ pub fn nasl_std_functions() -> Executor {
.add_set(description::Description)
.add_set(isotime::NaslIsotime)
.add_set(cryptographic::rc4::CipherHandlers::default())
.add_set(sys::Sys)
.add_set(ssh::Ssh::default())
.add_set(cert::NaslCerts::default());

Expand Down
162 changes: 162 additions & 0 deletions rust/src/nasl/builtin/sys/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
use std::{
env, io,
path::{Path, PathBuf},
};

use thiserror::Error;
use tokio::process::Command;

use crate::nasl::prelude::*;

#[derive(Error, Debug)]
pub enum SysError {
#[error("Failed to spawn process. {0}")]
SpawnProcess(io::Error),
#[error("Unable to read file. {0}")]
ReadFile(io::Error),
#[error("Unable to read file metadata. {0}")]
ReadFileMetadata(io::Error),
#[error("Unable to write file. {0}")]
WriteFile(io::Error),
#[error("Unable to remove file. {0}")]
RemoveFile(io::Error),
#[error("Error while trying to find the path for the command '{0}'")]
FindCommandPath(String),
#[error("Command '{0}' not found.")]
CommandNotFound(String),
}

pub struct Sys;

async fn find_path_of_command(cmd: &str) -> Result<PathBuf, SysError> {
// Here, we use `which` to find out
// what the path of the command is.
let make_err = || SysError::FindCommandPath(cmd.to_string());
let mut which_cmd = Command::new("which");
let output = which_cmd.arg(cmd).output().await.map_err(|_| make_err())?;
if output.status.success() {
let stdout = String::from_utf8(output.stdout).map_err(|_| make_err())?;
let path = Path::new(&stdout);
let dir = path.parent().ok_or_else(make_err)?.to_owned();
Ok(dir)
} else {
Err(SysError::CommandNotFound(cmd.to_string()))
}
}

#[nasl_function(named(cd))]
async fn pread(
cmd: &str,
cd: Option<bool>,
argv: CheckedPositionals<String>,
) -> Result<String, FnError> {
let mut real_cmd = Command::new(cmd);
if let Some(true) = cd {
// If `cd` is true, we need to change the cwd to
// the path in which the executable that will be
// run resides.
let dir = find_path_of_command(cmd).await?;
real_cmd.current_dir(dir);
};
for arg in argv.iter() {
real_cmd.arg(arg);
}
let out = real_cmd.output().await.map_err(SysError::SpawnProcess)?;
let stdout = String::from_utf8(out.stdout).unwrap();
Ok(stdout)
}

#[nasl_function]
async fn find_in_path(cmd: &str) -> Result<bool, FnError> {
let result = find_path_of_command(cmd).await;
match result {
Ok(_) => Ok(true),
Err(SysError::CommandNotFound(_)) => Ok(false),
Err(e) => Err(e.into()),
}
}

#[nasl_function]
async fn fread(path: &Path) -> Result<String, FnError> {
tokio::fs::read_to_string(path)
.await
.map_err(|e| SysError::ReadFile(e).into())
}

#[nasl_function(named(data, file))]
async fn fwrite(data: &str, file: &Path) -> Result<usize, FnError> {
tokio::fs::write(file, data)
.await
.map_err(SysError::WriteFile)?;
let num_bytes = data.len();
Ok(num_bytes)
}

#[nasl_function]
async fn file_stat(path: &Path) -> Result<u64, FnError> {
let metadata = tokio::fs::metadata(path)
.await
.map_err(SysError::ReadFileMetadata)?;
Ok(metadata.len())
}

#[nasl_function]
async fn unlink(path: &Path) -> Result<(), FnError> {
tokio::fs::remove_file(path)
.await
.map_err(|e| SysError::RemoveFile(e).into())
}

#[nasl_function]
async fn get_tmp_dir() -> PathBuf {
env::temp_dir()
}

function_set! {
Sys,
async_stateless,
(
pread,
fread,
file_stat,
find_in_path,
fwrite,
get_tmp_dir,
unlink,
)
}

#[cfg(test)]
mod tests {
use crate::nasl::{builtin::sys::SysError, test_prelude::*};

#[tokio::test]
async fn pread() {
let mut t = TestBuilder::default();
t.ok(r#"pread("basename", "/a/b/c");"#, "c\n");
t.async_verify().await;
}

#[tokio::test]
async fn find_in_path() {
let mut t = TestBuilder::default();
t.ok(r#"find_in_path("basename");"#, true);
// Cannot think of a way to construct a command name here
// that is very unlikely to exist without it sounding ridiculous
t.ok(r#"find_in_path("foobarbaz");"#, false);
t.async_verify().await;
}

#[tokio::test]
async fn write_read_to_tmpdir() {
let mut t = TestBuilder::default();
t.run(r#"path = get_tmp_dir();"#);
t.run(r#"file = path + "/write_read_to_tmpdir";"#);
t.ok(r#"fwrite(file: file, data: "foo");"#, 3);
t.ok(r#"fread(file);"#, "foo");
t.ok(r#"file_stat(file);"#, 3);
t.run(r#"unlink(file);"#);
check_err_matches!(t, r#"file_stat(file);"#, SysError::ReadFileMetadata(_));
t.async_verify().await;
}
}
13 changes: 12 additions & 1 deletion rust/src/nasl/builtin/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,17 @@ fn foo2(_register: &Register, x: usize) -> usize {
x
}

#[nasl_function]
fn add_positionals(_x: usize, _y: usize, argv: CheckedPositionals<usize>) -> usize {
argv.iter().sum()
}

struct Foo;

function_set! {
Foo,
sync_stateless,
(foo1, foo2)
(foo1, foo2, add_positionals)
}

/// Tests that the `Context` and `Register` arguments,
Expand All @@ -33,3 +38,9 @@ fn context_and_register_are_ignored_in_positional_index() {
t.ok("foo1(5);", 5);
t.ok("foo2(5);", 5);
}

#[test]
fn variadic_positionals_start_at_correct_index() {
let mut t = TestBuilder::default().with_executor(Executor::single(Foo));
t.ok("add_positionals(1, 2, 3, 4);", 7);
}
Loading

0 comments on commit 751266a

Please sign in to comment.