mirror of
https://github.com/helix-editor/helix.git
synced 2024-11-21 17:06:18 +04:00
Prevent improper files (like /dev/random) from being used as file arguments (#10733)
* Implement check before adding path to files
* fix problem where directories were removed from args.files
* Revert "Implement check before adding path to files"
This reverts commit c123944d9b
.
* Dissallow opening of irregular non-symlink files
* Fixed issue with creating new file from command line
* Fixed linting error.
* Optimized regularity check as suggested in review
* Created DocumentOpenError Sum Type to switch on in Application
* Forgot cargo fmt
* Update helix-term/src/application.rs
Accept suggestion in review.
Co-authored-by: Michael Davis <mcarsondavis@gmail.com>
* Moved thiserror version configuration to the workspace instead of the individual packages.
---------
Co-authored-by: Michael Davis <mcarsondavis@gmail.com>
This commit is contained in:
parent
d70f58da10
commit
94a9c81eb0
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -1535,6 +1535,7 @@ dependencies = [
|
||||
"serde_json",
|
||||
"slotmap",
|
||||
"tempfile",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"tokio-stream",
|
||||
"toml",
|
||||
|
@ -40,6 +40,7 @@ package.helix-term.opt-level = 2
|
||||
tree-sitter = { version = "0.22" }
|
||||
nucleo = "0.2.0"
|
||||
slotmap = "1.0.7"
|
||||
thiserror = "1.0"
|
||||
|
||||
[workspace.package]
|
||||
version = "24.3.0"
|
||||
|
@ -20,8 +20,8 @@ anyhow = "1.0"
|
||||
log = "0.4"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
thiserror = "1.0"
|
||||
tokio = { version = "1", features = ["rt", "rt-multi-thread", "io-util", "io-std", "time", "process", "macros", "fs", "parking_lot", "net", "sync"] }
|
||||
thiserror.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
fern = "0.6"
|
||||
|
@ -26,9 +26,9 @@ log = "0.4"
|
||||
lsp-types = { version = "0.95" }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
thiserror = "1.0"
|
||||
tokio = { version = "1.38", features = ["rt", "rt-multi-thread", "io-util", "io-std", "time", "process", "macros", "fs", "parking_lot", "sync"] }
|
||||
tokio-stream = "0.1.15"
|
||||
parking_lot = "0.12.3"
|
||||
arc-swap = "1"
|
||||
slotmap.workspace = true
|
||||
thiserror.workspace = true
|
||||
|
@ -9,7 +9,7 @@
|
||||
use helix_stdx::path::get_relative_path;
|
||||
use helix_view::{
|
||||
align_view,
|
||||
document::DocumentSavedEventResult,
|
||||
document::{DocumentOpenError, DocumentSavedEventResult},
|
||||
editor::{ConfigEvent, EditorEvent},
|
||||
graphics::Rect,
|
||||
theme,
|
||||
@ -186,9 +186,15 @@ pub fn new(args: Args, config: Config, lang_loader: syntax::Loader) -> Result<Se
|
||||
Some(Layout::Horizontal) => Action::HorizontalSplit,
|
||||
None => Action::Load,
|
||||
};
|
||||
let doc_id = editor
|
||||
.open(&file, action)
|
||||
.context(format!("open '{}'", file.to_string_lossy()))?;
|
||||
let doc_id = match editor.open(&file, action) {
|
||||
// Ignore irregular files during application init.
|
||||
Err(DocumentOpenError::IrregularFile) => {
|
||||
nr_of_files -= 1;
|
||||
continue;
|
||||
}
|
||||
Err(err) => return Err(anyhow::anyhow!(err)),
|
||||
Ok(doc_id) => doc_id,
|
||||
};
|
||||
// with Action::Load all documents have the same view
|
||||
// NOTE: this isn't necessarily true anymore. If
|
||||
// `--vsplit` or `--hsplit` are used, the file which is
|
||||
@ -199,15 +205,21 @@ pub fn new(args: Args, config: Config, lang_loader: syntax::Loader) -> Result<Se
|
||||
doc.set_selection(view_id, pos);
|
||||
}
|
||||
}
|
||||
editor.set_status(format!(
|
||||
"Loaded {} file{}.",
|
||||
nr_of_files,
|
||||
if nr_of_files == 1 { "" } else { "s" } // avoid "Loaded 1 files." grammo
|
||||
));
|
||||
// align the view to center after all files are loaded,
|
||||
// does not affect views without pos since it is at the top
|
||||
let (view, doc) = current!(editor);
|
||||
align_view(doc, view, Align::Center);
|
||||
|
||||
// if all files were invalid, replace with empty buffer
|
||||
if nr_of_files == 0 {
|
||||
editor.new_file(Action::VerticalSplit);
|
||||
} else {
|
||||
editor.set_status(format!(
|
||||
"Loaded {} file{}.",
|
||||
nr_of_files,
|
||||
if nr_of_files == 1 { "" } else { "s" } // avoid "Loaded 1 files." grammo
|
||||
));
|
||||
// align the view to center after all files are loaded,
|
||||
// does not affect views without pos since it is at the top
|
||||
let (view, doc) = current!(editor);
|
||||
align_view(doc, view, Align::Center);
|
||||
}
|
||||
} else {
|
||||
editor.new_file(Action::VerticalSplit);
|
||||
}
|
||||
|
@ -69,6 +69,7 @@
|
||||
use std::{
|
||||
cmp::Ordering,
|
||||
collections::{HashMap, HashSet},
|
||||
error::Error,
|
||||
fmt,
|
||||
future::Future,
|
||||
io::Read,
|
||||
|
@ -29,7 +29,7 @@
|
||||
|
||||
use helix_view::Editor;
|
||||
|
||||
use std::path::PathBuf;
|
||||
use std::{error::Error, path::PathBuf};
|
||||
|
||||
pub fn prompt(
|
||||
cx: &mut crate::commands::Context,
|
||||
|
@ -50,7 +50,7 @@ toml = "0.8"
|
||||
log = "~0.4"
|
||||
|
||||
parking_lot = "0.12.3"
|
||||
|
||||
thiserror.workspace = true
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
clipboard-win = { version = "5.3", features = ["std"] }
|
||||
|
@ -1,4 +1,4 @@
|
||||
use anyhow::{anyhow, bail, Context, Error};
|
||||
use anyhow::{anyhow, bail, Error};
|
||||
use arc_swap::access::DynAccess;
|
||||
use arc_swap::ArcSwap;
|
||||
use futures_util::future::BoxFuture;
|
||||
@ -12,6 +12,7 @@
|
||||
use helix_lsp::util::lsp_pos_to_pos;
|
||||
use helix_stdx::faccess::{copy_metadata, readonly};
|
||||
use helix_vcs::{DiffHandle, DiffProviderRegistry};
|
||||
use thiserror;
|
||||
|
||||
use ::parking_lot::Mutex;
|
||||
use serde::de::{self, Deserialize, Deserializer};
|
||||
@ -21,6 +22,7 @@
|
||||
use std::collections::HashMap;
|
||||
use std::fmt::Display;
|
||||
use std::future::Future;
|
||||
use std::io;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::str::FromStr;
|
||||
use std::sync::{Arc, Weak};
|
||||
@ -117,6 +119,14 @@ pub struct SavePoint {
|
||||
revert: Mutex<Transaction>,
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum DocumentOpenError {
|
||||
#[error("path must be a regular file, simlink, or directory")]
|
||||
IrregularFile,
|
||||
#[error(transparent)]
|
||||
IoError(#[from] io::Error),
|
||||
}
|
||||
|
||||
pub struct Document {
|
||||
pub(crate) id: DocumentId,
|
||||
text: Rope,
|
||||
@ -380,7 +390,7 @@ fn encode_from_utf8(
|
||||
pub fn from_reader<R: std::io::Read + ?Sized>(
|
||||
reader: &mut R,
|
||||
encoding: Option<&'static Encoding>,
|
||||
) -> Result<(Rope, &'static Encoding, bool), Error> {
|
||||
) -> Result<(Rope, &'static Encoding, bool), io::Error> {
|
||||
// These two buffers are 8192 bytes in size each and are used as
|
||||
// intermediaries during the decoding process. Text read into `buf`
|
||||
// from `reader` is decoded into `buf_out` as UTF-8. Once either
|
||||
@ -523,7 +533,7 @@ fn read_and_detect_encoding<R: std::io::Read + ?Sized>(
|
||||
reader: &mut R,
|
||||
encoding: Option<&'static Encoding>,
|
||||
buf: &mut [u8],
|
||||
) -> Result<(&'static Encoding, bool, encoding::Decoder, usize), Error> {
|
||||
) -> Result<(&'static Encoding, bool, encoding::Decoder, usize), io::Error> {
|
||||
let read = reader.read(buf)?;
|
||||
let is_empty = read == 0;
|
||||
let (encoding, has_bom) = encoding
|
||||
@ -685,11 +695,18 @@ pub fn open(
|
||||
encoding: Option<&'static Encoding>,
|
||||
config_loader: Option<Arc<ArcSwap<syntax::Loader>>>,
|
||||
config: Arc<dyn DynAccess<Config>>,
|
||||
) -> Result<Self, Error> {
|
||||
) -> Result<Self, DocumentOpenError> {
|
||||
// If the path is not a regular file (e.g.: /dev/random) it should not be opened.
|
||||
if path
|
||||
.metadata()
|
||||
.map_or(false, |metadata| !metadata.is_file())
|
||||
{
|
||||
return Err(DocumentOpenError::IrregularFile);
|
||||
}
|
||||
|
||||
// Open the file if it exists, otherwise assume it is a new file (and thus empty).
|
||||
let (rope, encoding, has_bom) = if path.exists() {
|
||||
let mut file =
|
||||
std::fs::File::open(path).context(format!("unable to open {:?}", path))?;
|
||||
let mut file = std::fs::File::open(path)?;
|
||||
from_reader(&mut file, encoding)?
|
||||
} else {
|
||||
let line_ending: LineEnding = config.load().default_line_ending.into();
|
||||
|
@ -1,6 +1,8 @@
|
||||
use crate::{
|
||||
align_view,
|
||||
document::{DocumentSavedEventFuture, DocumentSavedEventResult, Mode, SavePoint},
|
||||
document::{
|
||||
DocumentOpenError, DocumentSavedEventFuture, DocumentSavedEventResult, Mode, SavePoint,
|
||||
},
|
||||
graphics::{CursorKind, Rect},
|
||||
handlers::Handlers,
|
||||
info::Info,
|
||||
@ -1677,7 +1679,7 @@ pub fn new_file_from_stdin(&mut self, action: Action) -> Result<DocumentId, Erro
|
||||
}
|
||||
|
||||
// ??? possible use for integration tests
|
||||
pub fn open(&mut self, path: &Path, action: Action) -> Result<DocumentId, Error> {
|
||||
pub fn open(&mut self, path: &Path, action: Action) -> Result<DocumentId, DocumentOpenError> {
|
||||
let path = helix_stdx::path::canonicalize(path);
|
||||
let id = self.document_by_path(&path).map(|doc| doc.id);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user