Skip to content

Commit

Permalink
irulescan 1.1.0
Browse files Browse the repository at this point in the history
- preprocess irules to support laxer syntax (compared to tcl)
- add `table` command support
- fixed typo
- support rand() in expressions - avoid crashes
  • Loading branch information
simonkowallik committed Jan 21, 2023
1 parent c7ab07b commit 2dad3ca
Show file tree
Hide file tree
Showing 6 changed files with 119 additions and 15 deletions.
2 changes: 1 addition & 1 deletion files/apiserver.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ def decode(data: bytes) -> str:
],
description="[irulescan homepage](https://github.com/simonkowallik/irulescan/)",
title="irulescan",
version="1.0.0",
version="1.1.0",
docs_url="/",
)

Expand Down
39 changes: 38 additions & 1 deletion irulescan/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion irulescan/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]

name = "irulescan"
version = "1.0.0"
version = "1.1.0"
authors = ["Simon Kowallik <[email protected]>"]

build = "src/build.rs"
Expand All @@ -14,6 +14,7 @@ docopt = "1.1"
enum_primitive = "0.1"
num = "0.4"
rustc-serialize = "0.3"
fancy-regex = "0"

[[bin]]

Expand Down
67 changes: 65 additions & 2 deletions irulescan/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ extern crate libc;
// https://github.com/rust-lang/rust/issues/16920
#[macro_use] extern crate enum_primitive;
extern crate num;
extern crate fancy_regex;

use std::iter;
use std::fmt;
use fancy_regex::Regex;
use self::CheckResult::*; // TODO: why does swapping this line with one below break?
use rstcl::TokenType;

Expand Down Expand Up @@ -263,7 +265,7 @@ pub fn check_command<'a, 'b>(ctx: &'a str, tokens: &'b Vec<rstcl::TclToken<'a>>)
results.push(Danger(ctx, "missing options terminator `--` permits argument injection", tokens[i].val));
}
if (tokens.len() - i) != 2 {
results.push(Danger(ctx, "Dangerous unqoted switch body", tokens[i].val));
results.push(Danger(ctx, "Dangerous unquoted switch body", tokens[i].val));
}
param_types.extend_from_slice(&vec![Code::Normal, Code::SwitchBody]);
param_types
Expand Down Expand Up @@ -325,11 +327,42 @@ pub fn check_command<'a, 'b>(ctx: &'a str, tokens: &'b Vec<rstcl::TclToken<'a>>)
}
iter::repeat(Code::Normal).take(tokens.len()-1).collect()
},
// table set [-notouch] [-subtable <name> | -georedundancy] [-mustexist|-excl] <key> <value> [<timeout> [<lifetime>]]
// table add [-notouch] [-subtable <name> | -georedundancy] <key> <value> [<timeout> [<lifetime>]]
// table replace [-notouch] [-subtable <name> | -georedundancy] <key> <value> [<timeout> [<lifetime>]]
// table lookup [-notouch] [-subtable <name> | -georedundancy] <key>
// table incr [-notouch] [-subtable <name> | -georedundancy] [-mustexist] <key> [<delta>]
// table append [-notouch] [-subtable <name> | -georedundancy] [-mustexist] <key> <string>
// table delete [-subtable <name> | -georedundancy] <key>|-all
// table timeout [-subtable <name> | -georedundancy] [-remaining] <key>
// table timeout [-subtable <name> | -georedundancy] <key> [<value>]
// table lifetime [-subtable <name> | -georedundancy] [-remaining] <key>
// table lifetime [-subtable <name> | -georedundancy] <key> [<value>]
// table keys -subtable <name> [-count|-notouch]
"table" => {
let mut options_terminated = false;
let mut i = 1;
while i < tokens.len() {
match tokens[i].val {
"set"|"add"|"replace"|"lookup"|"incr"|"append"|"delete"|"timeout"|"lifetime"|
"-notouch"|"-georedundancy"|"-mustexist"|"-count"|"-remaining"|
"-excl" => { i += 1; },
"-subtable" => { i += 2; },
"keys" => { options_terminated = true; break; },
"--" => { options_terminated = true; break; },
_ => {break;},
};
};
if ! options_terminated {
results.push(Danger(ctx, "missing options terminator `--` permits argument injection", tokens[i].val));
}
iter::repeat(Code::Normal).take(tokens.len()-1).collect()
},
// default
_ => iter::repeat(Code::Normal).take(tokens.len()-1).collect(),
};
if param_types.len() != tokens.len() - 1 {
results.push(Warn(ctx, "badly formed command", tokens[0].val));
results.push(Danger(ctx, "badly formed command, cannot scan code", tokens[0].val));
return results;
}
for (param_type, param) in param_types.iter().zip(tokens[1..].iter()) {
Expand Down Expand Up @@ -426,3 +459,33 @@ pub fn scan_script<'a>(string: &'a str) -> Vec<CheckResult<'a>> {
}
return all_results;
}

/// Preprocess iRules to sanitize lax irule syntax
pub fn preprocess_script(string: &str) -> String {
fn re_replacer(s: &str, re: &Regex, t: &str) -> String {
re.replace_all(s, t).into()
}
let processed_script = &string;
//let processed_script = re_replacer(
// &processed_script,
// &Regex::new(r"(?<=[^\\])\\\s+\n").unwrap(),
// &r"\\\n"
//);
// HACK: rand() causes parsing errors, to avoid backtraces inject artificial parameter
let processed_script = re_replacer(
&processed_script,
&Regex::new(r"rand\(\)").unwrap(),
&r"rand($IRULESCAN)" // this would produce a TCL syntax error
);
let processed_script = re_replacer(
&processed_script,
&Regex::new(r"(?<!(\\|\{))\n[\s]*\{").unwrap(),
&r" {"
);
let processed_script = re_replacer(
&processed_script,
&Regex::new(r"(?P<token>\}|else|then|while|for)[\n\s]*\{").unwrap(),
&r"$token {"
);
return processed_script;
}
22 changes: 12 additions & 10 deletions irulescan/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ extern crate rustc_serialize;
extern crate docopt;
extern crate irulescan;

use std::error::Error;
use std::fs;
use std::io::prelude::*;
use std::io;
Expand All @@ -13,11 +12,13 @@ use docopt::Docopt;
use irulescan::rstcl;
use irulescan::CheckResult;

const USAGE: &'static str = "Usage: irulescan check [--no-warn] ( - | <path> )
const USAGE: &'static str = "
Usage: irulescan check [--no-warn] ( - | <path> )
irulescan parsestr ( - | <script-str> )
#
# details: https://github.com/simonkowallik/irulescan
# version: 1.0.0";
Additional Information:
home: https://github.com/simonkowallik/irulescan
version: 1.1.0
";

pub fn main() {
let args = Docopt::new(USAGE)
Expand All @@ -38,13 +39,13 @@ pub fn main() {
let path_display = path.display();
let mut file = match fs::File::open(&path) {
Err(err) => panic!("ERROR: Couldn't open {}: {}",
path_display, Error::description(&err)),
path_display, format!("{}", &err)),
Ok(file) => file,
};
let mut file_content = String::new();
match file.read_to_string(&mut file_content) {
Err(err) => panic!("ERROR: Couldn't read {}: {}",
path_display, Error::description(&err)),
path_display, format!("{}", &err)),
Ok(_) => file_content,
}
},
Expand All @@ -53,14 +54,14 @@ pub fn main() {
let mut stdin_content = String::new();
match io::stdin().read_to_string(&mut stdin_content) {
Err(err) => panic!("ERROR: Couldn't read stdin: {}",
Error::description(&err)),
format!("{}", &err)),
Ok(_) => stdin_content,
}
},
(false, true, false) => arg_script_str.to_owned(),
_ => panic!("Internal error: could not load script"),
};
let script = &script_in;
let script = &irulescan::preprocess_script(&script_in);
match (cmd_check, cmd_parsestr) {
(true, false) => {
let mut results = irulescan::scan_script(script);
Expand All @@ -71,7 +72,8 @@ pub fn main() {
}
if results.len() > 0 {
for check_result in results.iter() {
println!("{}", check_result);
// HACK: restore original rand() by removing artificial parameter
println!("{}", format!("{}",check_result).replace("rand($IRULESCAN)", "rand()"));
}
println!("");
};
Expand Down
1 change: 1 addition & 0 deletions irulescan/src/rstcl.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#![allow(deprecated)]
use std::mem::uninitialized;
use std::ffi::CString;

Expand Down

0 comments on commit 2dad3ca

Please sign in to comment.