Skip to content

Commit 3ba804c

Browse files
xe-nulRudxainmaribuDavid-Else
authored andcommitted
15735
PR helix-editor#15582 refactor(workspace_trust): minor changes fix(workspace_trust): secure permissions on Unix fix(workspace_trust): reject paths with \n PR helix-editor#15590 refactor(helix-loader): split out duplicated code In the workspace trust code, the code to search a path in a file was copy-pasted three times. This splits it out into a helper function. refactor(helix-loader): use BufReader to read trust files When the workspace trust files become large, using a BufReader can increase performance, as it won't block until the whole file is loaded into RAM. refactor(helix-loader): cache workspace trust, unify with PROMPTED_WORKSPACES Since `quick_query_workspace()` and `quick_query_workspace_with_explicit_untrust()` are currently called seven times on startup, it makes sense to cache the result. With this in place, the previous cache for prompted workspaces could be merged into this. Docs (helix-editor#15636) Improve readability and grammar of workspace trust docs and popup Rewrite of helix-editor#15592 Merge of helix-editor#15582 and rewritten helix-editor#15592 Change `PathBuf` to `Path`, as per suggestion from archseer Use workspace trust in git subsystem Replace default gitoxide's model of trust (directory ownership) with our own one. Closes helix-editor#15594 Merge of everything with helix-editor#15644 Expand docs; merge wirh everything else Hardening of workspace-trust-related file modes and permissions Do not query for trust if there are no langugage servers to start Refactor: save state into `Editor`; get rid of statics Add an option to disable trust selector pop-up Add `:workspace-exclude` typable command Update docs Add 'Only once' selector option and `:workspace-trust-once` typable Allow setting trust via globbed paths in config Co-authored-by: Rudxain <76864299+Rudxain@users.noreply.github.com> Co-authored-by: Marian Buschsieweke <marian.buschsieweke@posteo.net> Co-authored-by: David <12832280+David-Else@users.noreply.github.com>
1 parent 575be90 commit 3ba804c

29 files changed

Lines changed: 832 additions & 273 deletions

File tree

Cargo.lock

Lines changed: 3 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

book/src/editor.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
- [`[editor.smart-tab]` Section](#editorsmart-tab-section)
2424
- [`[editor.inline-diagnostics]` Section](#editorinline-diagnostics-section)
2525
- [`[editor.word-completion]` Section](#editorword-completion-section)
26+
- [`[editor.workspace-trust]` Section](#editorworkspace-trust-section)
2627

2728
### `[editor]` Section
2829

@@ -665,3 +666,27 @@ enable = true
665666
# Set the trigger length lower so that words are completed more often
666667
trigger-length = 4
667668
```
669+
670+
### `[editor.workspace-trust]` Section
671+
672+
Options for workspace trust.
673+
674+
| Key | Description | Default |
675+
| --- | --- | --- |
676+
| `level` | Level of implicit trust. Allowed values are: `"none"`, `"lsp"` and `"all"`. | `"none"` |
677+
| `selector` | Whether to show trust selector pop-up. | `true` |
678+
| `globs` | Globbed paths indicating trust or exclusion. See ["Workspace trust" section](./workspace-trust.md#glob-syntax). | [ ] |
679+
680+
Example:
681+
682+
```toml
683+
[editor.workspace-trust]
684+
# Always start LSP
685+
level = "lsp"
686+
687+
# Disable pop-up selector, leaving status bar reminder instead.
688+
selector = false
689+
690+
# Trust all workspaces in ~/work non-recursively
691+
globs = [ "~/work/*" ]
692+
```

book/src/generated/typable-cmd.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,8 @@
9797
| `:read`, `:r` | Load a file into buffer |
9898
| `:echo` | Prints the given arguments to the statusline. |
9999
| `:noop` | Does nothing. |
100-
| `:workspace-trust` | Add current workspace to the list of trusted workspaces. |
101-
| `:workspace-untrust` | Remove current workspace from the list of trusted workspaces. |
100+
| `:workspace-trust` | Trust current workspace permanently. |
101+
| `:workspace-untrust` | Untrust current workspace without excluding it. |
102+
| `:workspace-exclude` | Untrust current workspace and exclude it. |
103+
| `:workspace-trust-once` | Trust current workspace only until restart. |
102104
| `:set-max-width` | Set the maximum width of the editor, or swap between 2 widths. If set to 0 it will take up the entire width. |

book/src/workspace-trust.md

Lines changed: 61 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,73 @@
11
# Workspace trust
22

3-
Helix has a number of potentially dangerous features, namely LSP and ability to use local to workspace configurations. Those features can lead to unexpected code execution. To protect against code execution in dangerous contexts, Helix has a workspace trust protection, which will prevent these potentially dangerous features from running automatically.
3+
Helix includes two potentially dangerous features, both of which can execute arbitrary code:
44

5-
Helix will not trust any workspace by default.
5+
- Language servers (LSP)
6+
- Local workspace configurations (`.helix/config.toml` and `.helix/languages.toml`)
67

7-
By default, it will prompt about trust when you open new file in a workspace where you didn't make a decision about trust yet.
8+
To protect against this, Helix includes workspace trust protection, which prevents these features from running automatically unless the workspace is explicitly trusted.
89

9-
If you decide not to trust a workspace and don't want to be prompted about trust every time you start a new session in it, you can exclude the workspace by choosing `Never` option in trust selection window.
10+
## Default trust behavior
1011

11-
You can always make current workspace trusted by running `:workspace-trust` command, and untrust it with `:workspace-untrust`.
12+
Helix does not trust any workspace by default and will prompt you to choose the trust level when you open a file in a workspace where trust has not yet been set.
1213

13-
Lists of trusted and excluded workspaces, delimited by newline characters, are stored in `~/.local/share/helix/trusted_workspaces` and `~/.local/share/helix/excluded_workspaces` correspondingly.
14-
<!-- TODO: Windows paths -->
14+
## Changing workspace trust status
1515

16-
# Configuration
16+
You can always make the current workspace trusted by running the `:workspace-trust` command, and untrust it using `:workspace-untrust` or `:workspace-exclude`, with latter disabling all further prompts in it. If you wish to trust workspace only once, use `:workspace-trust-once`.
1717

18-
You can return to the old behaviour of loading every local `.helix/config.toml` and `.helix/languages.toml` and starting LSP's without an explicit permission by setting following option:
18+
Lists of trusted and excluded workspaces, delimited by newline characters, are stored in:
19+
20+
- Linux and macOS: `~/.local/share/helix/trusted_workspaces` and `~/.local/share/helix/excluded_workspaces`
21+
- Windows: `%AppData%\Roaming\helix\trusted_workspaces` and `%AppData%\Roaming\helix\excluded_workspaces`
22+
23+
24+
## Configuration
1925

2026
```toml
21-
[editor]
22-
insecure = true
27+
[editor.workspace-trust]
28+
# This option will disable workspace trust feature altogether.
29+
level = "all"
30+
31+
32+
# This will make Helix prompt only when there is local configuration
33+
# present in the workspace.
34+
# LSP will start automatically without an explicit confirmation.
35+
level = "lsp"
36+
37+
38+
# This is the default option.
39+
level = "none"
40+
41+
42+
# Disable pop-up selector, leaving status bar reminder instead.
43+
selector = false
44+
45+
46+
# Trust recursively every workspace in `~/work` and exclude every workspace
47+
# that contains "contrib" in its name.
48+
globs = [ "~/work/**", "!*contrib*" ]
2349
```
50+
51+
### Glob syntax
52+
53+
For more info about syntax see [globset docs](https://docs.rs/globset/latest/globset/#syntax)
54+
(`literal_separator` option is enabled).
55+
56+
Additional syntax:
57+
58+
- all `~/` will be expanded to `$HOME`;
59+
- globs prefixed with `!` will be excluded instead of trusted.
60+
61+
For example, this is valid:
62+
63+
````toml
64+
[editor]
65+
# Exclude `$HOME/coding/helix/contrib` and all subdirectories.
66+
workspace-trust.globs = [ "!{~/coding/helix/contrib/**,~/coding/helix/contrib}" ]
67+
68+
# Same thing, but with a smaller nesting.
69+
workspace-trust.globs = [ "!~/coding/helix/{contrib/**,contrib}" ]
70+
71+
# Same, but without nesting.
72+
workspace-trust.globs = [ "!~/coding/helix/contrib/**", "!~/coding/helix/contrib" ]
73+
````

helix-core/src/config.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use helix_loader::workspace_trust::WorkspaceTrust;
2+
13
use crate::{
24
auto_pairs::{AutoPairsRegistry, AutoPairsRegistryError},
35
syntax::{
@@ -47,8 +49,8 @@ impl std::fmt::Display for LanguageLoaderError {
4749
impl std::error::Error for LanguageLoaderError {}
4850

4951
/// Language configuration based on user configured languages.toml.
50-
pub fn user_lang_config(insecure: bool) -> Result<Configuration, toml::de::Error> {
51-
helix_loader::config::user_lang_config(insecure)?.try_into()
52+
pub fn user_lang_config(wst: &WorkspaceTrust) -> Result<Configuration, toml::de::Error> {
53+
helix_loader::config::user_lang_config(wst)?.try_into()
5254
}
5355

5456
/// Load the auto-pairs registry from auto-pairs.toml.
@@ -59,8 +61,8 @@ pub fn auto_pairs_registry() -> Result<AutoPairsRegistry, LanguageLoaderError> {
5961
}
6062

6163
/// Language configuration loader based on user configured languages.toml.
62-
pub fn user_lang_loader(insecure: bool) -> Result<Loader, LanguageLoaderError> {
63-
let config_val = helix_loader::config::user_lang_config(insecure)
64+
pub fn user_lang_loader(wst: &WorkspaceTrust) -> Result<Loader, LanguageLoaderError> {
65+
let config_val = helix_loader::config::user_lang_config(wst)
6466
.map_err(LanguageLoaderError::DeserializeError)?;
6567
let config = config_val.clone().try_into().map_err(|e| {
6668
if let Some(languages) = config_val.get("language").and_then(|v| v.as_array()) {

helix-loader/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ toml.workspace = true
2323
etcetera.workspace = true
2424
once_cell = "1.21"
2525
log = "0.4"
26+
parking_lot.workspace = true
27+
globset.workspace = true
2628

2729
# TODO: these two should be on !wasm32 only
2830

helix-loader/src/config.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use std::str::from_utf8;
22

3-
use crate::workspace_trust::{quick_query_workspace, TrustStatus};
3+
use crate::workspace_trust::{TrustStatus, WorkspaceTrust};
44

55
/// Default built-in languages.toml.
66
pub fn default_lang_config() -> toml::Value {
@@ -10,11 +10,12 @@ pub fn default_lang_config() -> toml::Value {
1010
}
1111

1212
/// User configured languages.toml file, merged with the default config.
13-
pub fn user_lang_config(insecure: bool) -> Result<toml::Value, toml::de::Error> {
13+
pub fn user_lang_config(ws: &WorkspaceTrust) -> Result<toml::Value, toml::de::Error> {
1414
let global_config = crate::lang_config_file();
1515
let workspace_config = crate::workspace_lang_config_file();
1616

17-
let files = if let TrustStatus::Trusted = quick_query_workspace(insecure) {
17+
let files = if TrustStatus::Trusted == ws.query_status(crate::workspace_trust::TrustType::Other)
18+
{
1819
vec![global_config, workspace_config]
1920
} else {
2021
vec![global_config]

helix-loader/src/grammar.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ use std::{
1111
use tempfile::TempPath;
1212
use tree_house::tree_sitter::Grammar;
1313

14+
use crate::workspace_trust::WorkspaceTrust;
15+
1416
#[cfg(target_os = "macos")]
1517
const DYLIB_EXTENSION: &str = "dylib";
1618

@@ -195,7 +197,8 @@ pub fn build_grammars(target: Option<String>) -> Result<()> {
195197
// merged. The `grammar_selection` key of the config is then used to filter
196198
// down all grammars into a subset of the user's choosing.
197199
fn get_grammar_configs() -> Result<Vec<GrammarConfiguration>> {
198-
let config: Configuration = crate::config::user_lang_config(false)
200+
let wst = WorkspaceTrust::new_bogus();
201+
let config: Configuration = crate::config::user_lang_config(&wst)
199202
.context("Could not parse languages.toml")?
200203
.try_into()?;
201204

@@ -216,8 +219,8 @@ fn get_grammar_configs() -> Result<Vec<GrammarConfiguration>> {
216219
Ok(grammars)
217220
}
218221

219-
pub fn get_grammar_names() -> Result<Option<HashSet<String>>> {
220-
let config: Configuration = crate::config::user_lang_config(false)
222+
pub fn get_grammar_names(wst: &WorkspaceTrust) -> Result<Option<HashSet<String>>> {
223+
let config: Configuration = crate::config::user_lang_config(wst)
221224
.context("Could not parse languages.toml")?
222225
.try_into()?;
223226

helix-loader/src/lib.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,11 @@ pub fn data_dir() -> PathBuf {
140140
path
141141
}
142142

143+
pub fn home_dir() -> PathBuf {
144+
let strategy = choose_base_strategy().expect("Unable to find home directory!");
145+
strategy.home_dir().to_path_buf()
146+
}
147+
143148
pub fn config_file() -> PathBuf {
144149
CONFIG_FILE.get().map(|path| path.to_path_buf()).unwrap()
145150
}

0 commit comments

Comments
 (0)