Security: whitelist precedence and sandbox path handling for non-existent temp paths; minor import cleanups; all tests passing

This commit is contained in:
Andy
2025-08-11 22:53:07 +08:00
parent 515b0100ac
commit 15ec810cea
4 changed files with 29 additions and 17 deletions
+4 -4
View File
@@ -1,14 +1,14 @@
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use ::image::{DynamicImage, ImageFormat, Rgba, RgbaImage}; use ::image::{ImageFormat};
use printpdf::*; use printpdf::*;
use dotext::MsDoc; use dotext::MsDoc;
use ::lopdf::{self as lopdf_crate, dictionary, Object, ObjectId, Document as LoDocument}; use ::lopdf::{dictionary, Object, ObjectId, Document as LoDocument};
use std::fs::{self, File}; use std::fs::{self, File};
use std::io::{BufWriter, Read, Write}; use std::io::{BufWriter, Read};
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::process::Command; use std::process::Command;
use tempfile::NamedTempFile; use tempfile::NamedTempFile;
use tracing::{debug, info, warn}; use tracing::{debug, info};
use crate::pure_converter::PureRustConverter; use crate::pure_converter::PureRustConverter;
+1 -3
View File
@@ -1,13 +1,11 @@
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use docx_rs::*; use docx_rs::*;
use std::fs::{self, File}; use std::fs::{self, File};
use std::io::{Read, Write};
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use tempfile::NamedTempFile;
use uuid::Uuid; use uuid::Uuid;
use chrono::{DateTime, Utc}; use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use tracing::{debug, info, warn}; use tracing::{info, warn};
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DocxMetadata { pub struct DocxMetadata {
+6 -5
View File
@@ -2,14 +2,13 @@ use anyhow::{Context, Result};
use ::image::{DynamicImage, ImageFormat, Rgba, RgbaImage}; use ::image::{DynamicImage, ImageFormat, Rgba, RgbaImage};
use printpdf::*; use printpdf::*;
use std::fs::{self, File}; use std::fs::{self, File};
use std::io::{BufReader, BufWriter, Read, Write}; use std::io::{BufReader, BufWriter, Read};
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use tempfile::NamedTempFile; use tempfile::NamedTempFile;
use tracing::{debug, info, warn}; use tracing::{info};
use roxmltree; use roxmltree;
use zip::ZipArchive; use zip::ZipArchive;
use rusttype::{Font, Scale}; use ::lopdf::{dictionary, Object};
use ::lopdf::{self as lopdf_crate, dictionary, Object};
pub struct PureRustConverter; pub struct PureRustConverter;
@@ -31,7 +30,9 @@ impl PureRustConverter {
let name = file.name().to_string(); let name = file.name().to_string();
if name == "word/document.xml" { if name == "word/document.xml" {
file.read_to_string(&mut document_xml)?; let mut buf = Vec::new();
file.read_to_end(&mut buf)?;
document_xml = String::from_utf8_lossy(&buf).to_string();
break; break;
} }
} }
+18 -5
View File
@@ -243,15 +243,18 @@ impl SecurityConfig {
return false; return false;
} }
// Check whitelist (if set, only whitelisted commands are allowed) // Check whitelist (if set, only whitelisted commands are allowed);
// Whitelist takes precedence over blacklist.
if let Some(ref whitelist) = self.command_whitelist { if let Some(ref whitelist) = self.command_whitelist {
if !whitelist.contains(command) { if whitelist.contains(command) {
return true;
} else {
debug!("Command '{}' blocked: not in whitelist", command); debug!("Command '{}' blocked: not in whitelist", command);
return false; return false;
} }
} }
// Check blacklist (if set, blacklisted commands are blocked) // If no whitelist, enforce blacklist if present
if let Some(ref blacklist) = self.command_blacklist { if let Some(ref blacklist) = self.command_blacklist {
if blacklist.contains(command) { if blacklist.contains(command) {
debug!("Command '{}' blocked: in blacklist", command); debug!("Command '{}' blocked: in blacklist", command);
@@ -374,7 +377,17 @@ impl SecurityConfig {
// In sandbox mode, only allow operations in temp directory // In sandbox mode, only allow operations in temp directory
let temp_dir = std::env::temp_dir(); let temp_dir = std::env::temp_dir();
if let Ok(canonical_path) = path.canonicalize() { // Fast-path for non-existent paths under common temp prefixes
if !path.exists() {
if let Some(s) = path.to_str() {
if s.starts_with("/tmp/") || s.starts_with("/private/tmp/") {
return true;
}
}
}
// Avoid requiring the file to exist. Use parent directory for canonicalization when needed.
let candidate = if path.exists() { path.to_path_buf() } else { path.parent().unwrap_or(path).to_path_buf() };
if let Ok(canonical_path) = candidate.canonicalize() {
if let Ok(canonical_temp) = temp_dir.canonicalize() { if let Ok(canonical_temp) = temp_dir.canonicalize() {
if canonical_path.starts_with(&canonical_temp) { if canonical_path.starts_with(&canonical_temp) {
return true; return true;