WIP: Add a setting to visually redact enviroment variables (#7124)

Release Notes:

- Added bash syntax highlighting to `.env` files. 
- Added a `private_files` setting for configuring which files should be
considered to contain environment variables or other sensitive
information.
- Added a `redact_private_values` setting to add or remove censor bars
over variable values in files matching the `private_files` patterns.
-(internal) added a new `redactions.scm` query to our language support,
allowing different config file formats to indicate where environment
variable values can be identified in the syntax tree, added this query
to `bash`, `json`, `toml`, and `yaml` files.

---------

Co-authored-by: Nathan <nathan@zed.dev>
This commit is contained in:
Mikayla Maki 2024-01-31 11:42:09 -08:00 committed by GitHub
parent 5333eff0e4
commit f98d636203
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
23 changed files with 330 additions and 32 deletions

View file

@ -383,6 +383,9 @@ pub trait File: Send + Sync {
/// Converts this file into a protobuf message.
fn to_proto(&self) -> rpc::proto::File;
/// Return whether Zed considers this to be a dotenv file.
fn is_private(&self) -> bool;
}
/// The file associated with a buffer, in the case where the file is on the local disk.
@ -2877,6 +2880,43 @@ impl BufferSnapshot {
})
}
/// Returns anchor ranges for any matches of the redaction query.
/// The buffer can be associated with multiple languages, and the redaction query associated with each
/// will be run on the relevant section of the buffer.
pub fn redacted_ranges<'a, T: ToOffset>(
&'a self,
range: Range<T>,
) -> impl Iterator<Item = Range<usize>> + 'a {
let offset_range = range.start.to_offset(self)..range.end.to_offset(self);
let mut syntax_matches = self.syntax.matches(offset_range, self, |grammar| {
grammar
.redactions_config
.as_ref()
.map(|config| &config.query)
});
let configs = syntax_matches
.grammars()
.iter()
.map(|grammar| grammar.redactions_config.as_ref())
.collect::<Vec<_>>();
iter::from_fn(move || {
let redacted_range = syntax_matches
.peek()
.and_then(|mat| {
configs[mat.grammar_index].and_then(|config| {
mat.captures
.iter()
.find(|capture| capture.index == config.redaction_capture_ix)
})
})
.map(|mat| mat.node.byte_range());
syntax_matches.advance();
redacted_range
})
}
/// Returns selections for remote peers intersecting the given range.
#[allow(clippy::type_complexity)]
pub fn remote_selections_in_range(

View file

@ -453,6 +453,7 @@ pub struct LanguageQueries {
pub embedding: Option<Cow<'static, str>>,
pub injections: Option<Cow<'static, str>>,
pub overrides: Option<Cow<'static, str>>,
pub redactions: Option<Cow<'static, str>>,
}
/// Represents a language for the given range. Some languages (e.g. HTML)
@ -623,6 +624,7 @@ pub struct Grammar {
pub(crate) error_query: Query,
pub(crate) highlights_query: Option<Query>,
pub(crate) brackets_config: Option<BracketConfig>,
pub(crate) redactions_config: Option<RedactionConfig>,
pub(crate) indents_config: Option<IndentConfig>,
pub outline_config: Option<OutlineConfig>,
pub embedding_config: Option<EmbeddingConfig>,
@ -664,6 +666,11 @@ struct InjectionConfig {
patterns: Vec<InjectionPatternConfig>,
}
struct RedactionConfig {
pub query: Query,
pub redaction_capture_ix: u32,
}
struct OverrideConfig {
query: Query,
values: HashMap<u32, (String, LanguageConfigOverride)>,
@ -1303,6 +1310,7 @@ impl Language {
indents_config: None,
injection_config: None,
override_config: None,
redactions_config: None,
error_query: Query::new(&ts_language, "(ERROR) @error").unwrap(),
ts_language,
highlight_map: Default::default(),
@ -1359,6 +1367,11 @@ impl Language {
.with_override_query(query.as_ref())
.context("Error loading override query")?;
}
if let Some(query) = queries.redactions {
self = self
.with_redaction_query(query.as_ref())
.context("Error loading redaction query")?;
}
Ok(self)
}
@ -1589,6 +1602,22 @@ impl Language {
Ok(self)
}
pub fn with_redaction_query(mut self, source: &str) -> anyhow::Result<Self> {
let grammar = self.grammar_mut();
let query = Query::new(&grammar.ts_language, source)?;
let mut redaction_capture_ix = None;
get_capture_indices(&query, &mut [("redact", &mut redaction_capture_ix)]);
if let Some(redaction_capture_ix) = redaction_capture_ix {
grammar.redactions_config = Some(RedactionConfig {
query,
redaction_capture_ix,
});
}
Ok(self)
}
fn grammar_mut(&mut self) -> &mut Grammar {
Arc::get_mut(self.grammar.as_mut().unwrap()).unwrap()
}

View file

@ -1059,7 +1059,7 @@ impl<'a> SyntaxMapMatches<'a> {
.position(|later_layer| key < later_layer.sort_key())
.unwrap_or(self.active_layer_count - 1);
self.layers[0..i].rotate_left(1);
} else {
} else if self.active_layer_count != 0 {
self.layers[0..self.active_layer_count].rotate_left(1);
self.active_layer_count -= 1;
}