Security: whitelist precedence and sandbox path handling for non-existent temp paths; minor import cleanups; all tests passing
This commit is contained in:
+4
-4
@@ -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
@@ -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 {
|
||||||
|
|||||||
@@ -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
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user