Skip to content
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

feat!: force users to explicitly enable code execution #276

Merged
merged 2 commits into from
Jul 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions config-file-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@
"options": {
"$ref": "#/definitions/OptionsConfig"
},
"snippet": {
"$ref": "#/definitions/SnippetConfig"
},
"typst": {
"$ref": "#/definitions/TypstConfig"
}
Expand Down Expand Up @@ -262,6 +265,33 @@
},
"additionalProperties": false
},
"SnippetConfig": {
"type": "object",
"properties": {
"exec": {
"description": "The properties for snippet execution.",
"allOf": [
{
"$ref": "#/definitions/SnippetExecConfig"
}
]
}
},
"additionalProperties": false
},
"SnippetExecConfig": {
"type": "object",
"required": [
"enable"
],
"properties": {
"enable": {
"description": "Whether to enable snippet execution.",
"type": "boolean"
}
},
"additionalProperties": false
},
"TypstConfig": {
"type": "object",
"properties": {
Expand Down
18 changes: 18 additions & 0 deletions src/custom.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ pub struct Config {

#[serde(default)]
pub bindings: KeyBindingsConfig,

#[serde(default)]
pub snippet: SnippetConfig,
}

impl Config {
Expand Down Expand Up @@ -114,6 +117,21 @@ pub struct OptionsConfig {
pub strict_front_matter_parsing: Option<bool>,
}

#[derive(Clone, Debug, Default, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct SnippetConfig {
/// The properties for snippet execution.
#[serde(default)]
pub exec: SnippetExecConfig,
}

#[derive(Clone, Debug, Default, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct SnippetExecConfig {
/// Whether to enable snippet execution.
pub enable: bool,
}

#[derive(Clone, Debug, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct TypstConfig {
Expand Down
8 changes: 8 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ struct Cli {
#[clap(long)]
validate_overflows: bool,

/// Enable code snippet execution.
#[clap(short = 'x', long)]
enable_snippet_execution: bool,

/// The path to the configuration file.
#[clap(short, long)]
config_file: Option<String>,
Expand Down Expand Up @@ -135,6 +139,7 @@ fn make_builder_options(config: &Config, mode: &PresentMode, force_default_theme
end_slide_shorthand: config.options.end_slide_shorthand.unwrap_or_default(),
print_modal_background: false,
strict_front_matter_parsing: config.options.strict_front_matter_parsing.unwrap_or(true),
enable_snippet_execution: config.snippet.exec.enable,
}
}

Expand Down Expand Up @@ -208,6 +213,9 @@ fn run(mut cli: Cli) -> Result<(), Box<dyn std::error::Error>> {
let validate_overflows = overflow_validation(&mode, &config.defaults.validate_overflows) || cli.validate_overflows;
let resources_path = path.parent().unwrap_or(Path::new("/"));
let mut options = make_builder_options(&config, &mode, force_default_theme);
if cli.enable_snippet_execution {
options.enable_snippet_execution = true;
}
let graphics_mode = select_graphics_mode(&cli, &config);
let printer = Arc::new(ImagePrinter::new(graphics_mode.clone())?);
let registry = ImageRegistry(printer.clone());
Expand Down
35 changes: 32 additions & 3 deletions src/processing/builder.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use super::modals::KeyBindingsModalBuilder;
use crate::{
custom::{KeyBindingsConfig, OptionsConfig},
execute::CodeExecutor,
Expand Down Expand Up @@ -33,8 +34,6 @@ use serde::Deserialize;
use std::{borrow::Cow, cell::RefCell, fmt::Display, iter, mem, path::PathBuf, rc::Rc, str::FromStr};
use unicode_width::UnicodeWidthStr;

use super::modals::KeyBindingsModalBuilder;

// TODO: move to a theme config.
static DEFAULT_BOTTOM_SLIDE_MARGIN: u16 = 3;
pub(crate) static DEFAULT_IMAGE_Z_INDEX: i32 = -2;
Expand All @@ -55,6 +54,7 @@ pub struct PresentationBuilderOptions {
pub end_slide_shorthand: bool,
pub print_modal_background: bool,
pub strict_front_matter_parsing: bool,
pub enable_snippet_execution: bool,
}

impl PresentationBuilderOptions {
Expand All @@ -81,6 +81,7 @@ impl Default for PresentationBuilderOptions {
end_slide_shorthand: false,
print_modal_background: false,
strict_front_matter_parsing: true,
enable_snippet_execution: false,
}
}
}
Expand Down Expand Up @@ -687,7 +688,7 @@ impl<'a> PresentationBuilder<'a> {
if self.options.allow_mutations && context.borrow().groups.len() > 1 {
self.chunk_mutators.push(Box::new(HighlightMutator::new(context)));
}
if code.attributes.execute {
if code.attributes.execute && self.options.enable_snippet_execution {
self.push_code_execution(code)?;
}
Ok(())
Expand Down Expand Up @@ -1047,6 +1048,7 @@ impl From<StrictPresentationMetadata> for PresentationMetadata {
#[cfg(test)]
mod test {
use super::*;
use crate::markdown::elements::CodeAttributes;
use rstest::rstest;

fn build_presentation(elements: Vec<MarkdownElement>) -> Presentation {
Expand Down Expand Up @@ -1464,4 +1466,31 @@ mod test {
let result = try_build_presentation_with_options(elements, options);
assert!(result.is_ok());
}

#[rstest]
#[case::enabled(true)]
#[case::disabled(false)]
fn snippet_execution(#[case] enabled: bool) {
let element = MarkdownElement::Code(Code {
contents: "".into(),
language: CodeLanguage::Rust,
attributes: CodeAttributes { execute: true, ..Default::default() },
});
let options = PresentationBuilderOptions { enable_snippet_execution: enabled, ..Default::default() };
let presentation = build_presentation_with_options(vec![element], options);
let slide = presentation.iter_slides().next().unwrap();
let mut found_render_async = false;
for operation in slide.iter_operations() {
let is_render_async = matches!(operation, RenderOperation::RenderAsync(_));
if is_render_async {
assert!(enabled);
found_render_async = true;
}
}
if found_render_async {
assert!(enabled, "code execution block found but not enabled");
} else {
assert!(!enabled, "code execution enabled but not found");
}
}
}
Loading