diff --git a/README.md b/README.md index de7aca1..1022962 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,14 @@ cargo install hexo Use `hexo -h` to get complete CLI manual. +### Arguments + +- `log-level` + : Set log level, possible values: `debug`, `info`, `warn`, `error`, `none`. Default: `info`. + +- `safe` + : Enable safe mode, will disable unsafe functions like `cmd` and `eval`. Default: `false` + ### Commands #### build @@ -37,7 +45,6 @@ Takes `source` file in hexo format and compiles it to binary file `output`. Will hexo watch --source --output ``` - ## Syntax ### Emitter @@ -142,4 +149,6 @@ Not currently, but this is planned for future releases. ### Isn't `cmd` function highly unsafe? Yes, safety is no goal of Hexo, please don't run untrusted code with it. -Safe mode might be implemented in the future is there is enough demand for it. + +You can use safe mode to disable `cmd` and `eval` functions. +Please add `--safe` flag before command to enable it. \ No newline at end of file diff --git a/src/compiler/compiler_context.rs b/src/compiler/compiler_context.rs index ba92bd4..d328e05 100644 --- a/src/compiler/compiler_context.rs +++ b/src/compiler/compiler_context.rs @@ -8,4 +8,8 @@ impl HexoCompilerContext { safe_mode } } + + pub(crate) fn safe_mode(&self) -> bool { + self.safe_mode + } } diff --git a/src/compiler/hexo_compiler.rs b/src/compiler/hexo_compiler.rs index 9c166db..ba04f3d 100644 --- a/src/compiler/hexo_compiler.rs +++ b/src/compiler/hexo_compiler.rs @@ -38,7 +38,7 @@ impl HexoCompiler { source: &TSource, ) -> Result { let cst = self.compile_cst(source)?; - let rst_compiler = RstCompiler::new(self); + let rst_compiler = RstCompiler::new(self, self.context.safe_mode()); rst_compiler.compile(&cst).map_err(Error::Rst) } diff --git a/src/compiler/native_fn/implementations.rs b/src/compiler/native_fn/implementations.rs index 8d23f7c..1e0eceb 100644 --- a/src/compiler/native_fn/implementations.rs +++ b/src/compiler/native_fn/implementations.rs @@ -51,7 +51,7 @@ pub(crate) fn create_pad_right_native_function() -> NativeFunction { pub(crate) fn create_cmd_native_function() -> NativeFunction { NativeFunction::new( - NativeFunctionSignature::new("cmd"), + NativeFunctionSignature::new_unsafe("cmd"), |arguments, _| { let command = get_argument_at(&arguments, 0, "cmd")? .to_string() @@ -116,7 +116,7 @@ pub(crate) fn create_pad_native_function() -> NativeFunction { pub(crate) fn create_eval_native_function() -> NativeFunction { NativeFunction::new( - NativeFunctionSignature::new("eval"), + NativeFunctionSignature::new_unsafe("eval"), |arguments, compiler| { let buffer = get_argument_at(&arguments, 0, "eval")?.clone(); let source = LiteralCompilerSource::anonymous( diff --git a/src/compiler/native_fn/index.rs b/src/compiler/native_fn/index.rs index c34ff21..6ba422a 100644 --- a/src/compiler/native_fn/index.rs +++ b/src/compiler/native_fn/index.rs @@ -7,6 +7,7 @@ pub(crate) struct NativeFunctionIndex { } impl NativeFunctionIndex { + pub(crate) fn new() -> NativeFunctionIndex { NativeFunctionIndex { functions: Self::create_native_functions(), @@ -14,7 +15,7 @@ impl NativeFunctionIndex { } pub(crate) fn find(&self, name: String) -> Option<&NativeFunction> { - return self.functions.iter().find(|f| f.signature().name == name); + return self.functions.iter().find(|f| f.signature().name() == name); } fn create_native_functions() -> Vec { diff --git a/src/compiler/native_fn/signature.rs b/src/compiler/native_fn/signature.rs index 91c0618..12e5e6d 100644 --- a/src/compiler/native_fn/signature.rs +++ b/src/compiler/native_fn/signature.rs @@ -5,15 +5,32 @@ use crate::compiler::HexoCompiler; #[derive(Clone, Debug)] pub(crate) struct NativeFunctionSignature { - pub(crate) name: String, + name: String, + is_safe: bool } impl NativeFunctionSignature { pub(crate) fn new(name: &str) -> NativeFunctionSignature { NativeFunctionSignature { name: String::from(name), + is_safe: true } } + + pub(crate) fn new_unsafe(name: &str) -> NativeFunctionSignature { + NativeFunctionSignature { + name: String::from(name), + is_safe: false + } + } + + pub(crate) fn name(&self) -> &str { + &self.name + } + + pub(crate) fn is_safe(&self) -> bool { + self.is_safe + } } type NativeFunctionExecutor = fn(&HashMap, &HexoCompiler) -> Result; diff --git a/src/compiler/rst/compiler.rs b/src/compiler/rst/compiler.rs index ba23cb8..def6ea3 100644 --- a/src/compiler/rst/compiler.rs +++ b/src/compiler/rst/compiler.rs @@ -14,11 +14,12 @@ use crate::util::id::HexoId; pub(crate) struct RstCompiler<'a> { parent: &'a HexoCompiler, + safe_mode: bool, } impl RstCompiler<'_> { - pub(crate) fn new(parent: &HexoCompiler) -> RstCompiler { - RstCompiler { parent } + pub(crate) fn new(parent: &HexoCompiler, safe_mode: bool) -> RstCompiler { + RstCompiler { parent, safe_mode } } pub(crate) fn compile(&self, cst: &CstFile) -> Result { @@ -83,6 +84,14 @@ impl RstCompiler<'_> { ) -> Result<(), Error> { let native_function = context.get_native_function(function_name.as_str()); if let Some(native_function) = native_function { + if self.safe_mode && !native_function.signature().is_safe() { + return Err( + Error::NativeFunctionIsUnsafe { + name: native_function.signature().name().to_string() + } + ) + } + let executor = native_function.executor(); let mut params_buffer = HashMap::new(); diff --git a/src/compiler/rst/error.rs b/src/compiler/rst/error.rs index 544b35d..319e188 100644 --- a/src/compiler/rst/error.rs +++ b/src/compiler/rst/error.rs @@ -3,6 +3,7 @@ pub(crate) enum Error { UnresolvedConstant { name: String }, UnresolvedFunction { name: String }, NativeFunctionExecution(crate::compiler::native_fn::Error), + NativeFunctionIsUnsafe { name: String }, } impl std::fmt::Display for Error { @@ -17,6 +18,9 @@ impl std::fmt::Display for Error { Error::NativeFunctionExecution(e) => { write!(f, "Native function execution error: {}", e) } + Error::NativeFunctionIsUnsafe { name } => { + write!(f, "Native function is unsafe: {}", name) + } } } }