Sanitize ranges in code labels coming from extensions (#10307)
Without any sanitization, extensions would be able to crash zed, because the editor code assumes these ranges are valid. Release Notes: - N/A
This commit is contained in:
parent
a4566c36a3
commit
cc367d43d6
2 changed files with 91 additions and 39 deletions
|
@ -292,13 +292,13 @@ fn labels_from_wit(
|
||||||
labels
|
labels
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|label| {
|
.map(|label| {
|
||||||
label.map(|label| {
|
let label = label?;
|
||||||
build_code_label(
|
let runs = if !label.code.is_empty() {
|
||||||
&label,
|
language.highlight_text(&label.code.as_str().into(), 0..label.code.len())
|
||||||
&language.highlight_text(&label.code.as_str().into(), 0..label.code.len()),
|
} else {
|
||||||
&language,
|
Vec::new()
|
||||||
)
|
};
|
||||||
})
|
build_code_label(&label, &runs, &language)
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
@ -307,7 +307,7 @@ fn build_code_label(
|
||||||
label: &wit::CodeLabel,
|
label: &wit::CodeLabel,
|
||||||
parsed_runs: &[(Range<usize>, HighlightId)],
|
parsed_runs: &[(Range<usize>, HighlightId)],
|
||||||
language: &Arc<Language>,
|
language: &Arc<Language>,
|
||||||
) -> CodeLabel {
|
) -> Option<CodeLabel> {
|
||||||
let mut text = String::new();
|
let mut text = String::new();
|
||||||
let mut runs = vec![];
|
let mut runs = vec![];
|
||||||
|
|
||||||
|
@ -315,7 +315,7 @@ fn build_code_label(
|
||||||
match span {
|
match span {
|
||||||
wit::CodeLabelSpan::CodeRange(range) => {
|
wit::CodeLabelSpan::CodeRange(range) => {
|
||||||
let range = Range::from(*range);
|
let range = Range::from(*range);
|
||||||
|
let code_span = &label.code.get(range.clone())?;
|
||||||
let mut input_ix = range.start;
|
let mut input_ix = range.start;
|
||||||
let mut output_ix = text.len();
|
let mut output_ix = text.len();
|
||||||
for (run_range, id) in parsed_runs {
|
for (run_range, id) in parsed_runs {
|
||||||
|
@ -327,19 +327,18 @@ fn build_code_label(
|
||||||
}
|
}
|
||||||
|
|
||||||
if run_range.start > input_ix {
|
if run_range.start > input_ix {
|
||||||
output_ix += run_range.start - input_ix;
|
let len = run_range.start - input_ix;
|
||||||
input_ix = run_range.start;
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
let len = range.end.min(run_range.end) - input_ix;
|
|
||||||
runs.push((output_ix..output_ix + len, *id));
|
|
||||||
output_ix += len;
|
output_ix += len;
|
||||||
input_ix += len;
|
input_ix += len;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let len = range.end.min(run_range.end) - input_ix;
|
||||||
|
runs.push((output_ix..output_ix + len, *id));
|
||||||
|
output_ix += len;
|
||||||
|
input_ix += len;
|
||||||
}
|
}
|
||||||
|
|
||||||
text.push_str(&label.code[range]);
|
text.push_str(code_span);
|
||||||
}
|
}
|
||||||
wit::CodeLabelSpan::Literal(span) => {
|
wit::CodeLabelSpan::Literal(span) => {
|
||||||
let highlight_id = language
|
let highlight_id = language
|
||||||
|
@ -356,11 +355,13 @@ fn build_code_label(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CodeLabel {
|
let filter_range = Range::from(label.filter_range);
|
||||||
|
text.get(filter_range.clone())?;
|
||||||
|
Some(CodeLabel {
|
||||||
text,
|
text,
|
||||||
runs,
|
runs,
|
||||||
filter_range: label.filter_range.into(),
|
filter_range,
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<wit::Range> for Range<usize> {
|
impl From<wit::Range> for Range<usize> {
|
||||||
|
@ -472,13 +473,13 @@ fn extract_int<T: Serialize>(value: T) -> i32 {
|
||||||
fn test_build_code_label() {
|
fn test_build_code_label() {
|
||||||
use util::test::marked_text_ranges;
|
use util::test::marked_text_ranges;
|
||||||
|
|
||||||
let (code, ranges) = marked_text_ranges(
|
let (code, code_ranges) = marked_text_ranges(
|
||||||
"«const» «a»: «fn»(«Bcd»(«Efgh»)) -> «Ijklm» = pqrs.tuv",
|
"«const» «a»: «fn»(«Bcd»(«Efgh»)) -> «Ijklm» = pqrs.tuv",
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
let runs = ranges
|
let code_runs = code_ranges
|
||||||
.iter()
|
.into_iter()
|
||||||
.map(|range| (range.clone(), HighlightId(0)))
|
.map(|range| (range, HighlightId(0)))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
let label = build_code_label(
|
let label = build_code_label(
|
||||||
|
@ -499,22 +500,75 @@ fn test_build_code_label() {
|
||||||
},
|
},
|
||||||
code,
|
code,
|
||||||
},
|
},
|
||||||
&runs,
|
&code_runs,
|
||||||
&language::PLAIN_TEXT,
|
&language::PLAIN_TEXT,
|
||||||
);
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let (text, ranges) = marked_text_ranges("pqrs.tuv: «fn»(«Bcd»(«Efgh»)) -> «Ijklm»", false);
|
let (label_text, label_ranges) =
|
||||||
let runs = ranges
|
marked_text_ranges("pqrs.tuv: «fn»(«Bcd»(«Efgh»)) -> «Ijklm»", false);
|
||||||
.iter()
|
let label_runs = label_ranges
|
||||||
.map(|range| (range.clone(), HighlightId(0)))
|
.into_iter()
|
||||||
|
.map(|range| (range, HighlightId(0)))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
label,
|
label,
|
||||||
CodeLabel {
|
CodeLabel {
|
||||||
text,
|
text: label_text,
|
||||||
runs,
|
runs: label_runs,
|
||||||
filter_range: label.filter_range.clone()
|
filter_range: label.filter_range.clone()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_build_code_label_with_invalid_ranges() {
|
||||||
|
use util::test::marked_text_ranges;
|
||||||
|
|
||||||
|
let (code, code_ranges) = marked_text_ranges("const «a»: «B» = '🏀'", false);
|
||||||
|
let code_runs = code_ranges
|
||||||
|
.into_iter()
|
||||||
|
.map(|range| (range, HighlightId(0)))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
// A span uses a code range that is invalid because it starts inside of
|
||||||
|
// a multi-byte character.
|
||||||
|
let label = build_code_label(
|
||||||
|
&wit::CodeLabel {
|
||||||
|
spans: vec![
|
||||||
|
wit::CodeLabelSpan::CodeRange(wit::Range {
|
||||||
|
start: code.find('B').unwrap() as u32,
|
||||||
|
end: code.find(" = ").unwrap() as u32,
|
||||||
|
}),
|
||||||
|
wit::CodeLabelSpan::CodeRange(wit::Range {
|
||||||
|
start: code.find('🏀').unwrap() as u32 + 1,
|
||||||
|
end: code.len() as u32,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
filter_range: wit::Range {
|
||||||
|
start: 0,
|
||||||
|
end: "B".len() as u32,
|
||||||
|
},
|
||||||
|
code,
|
||||||
|
},
|
||||||
|
&code_runs,
|
||||||
|
&language::PLAIN_TEXT,
|
||||||
|
);
|
||||||
|
assert!(label.is_none());
|
||||||
|
|
||||||
|
// Filter range extends beyond actual text
|
||||||
|
let label = build_code_label(
|
||||||
|
&wit::CodeLabel {
|
||||||
|
spans: vec![wit::CodeLabelSpan::Literal(wit::CodeLabelSpanLiteral {
|
||||||
|
text: "abc".into(),
|
||||||
|
highlight_name: Some("type".into()),
|
||||||
|
})],
|
||||||
|
filter_range: wit::Range { start: 0, end: 5 },
|
||||||
|
code: String::new(),
|
||||||
|
},
|
||||||
|
&code_runs,
|
||||||
|
&language::PLAIN_TEXT,
|
||||||
|
);
|
||||||
|
assert!(label.is_none());
|
||||||
|
}
|
||||||
|
|
|
@ -1,22 +1,20 @@
|
||||||
mod since_v0_0_1;
|
mod since_v0_0_1;
|
||||||
mod since_v0_0_4;
|
mod since_v0_0_4;
|
||||||
mod since_v0_0_6;
|
mod since_v0_0_6;
|
||||||
|
use since_v0_0_6 as latest;
|
||||||
|
|
||||||
use std::ops::RangeInclusive;
|
use super::{wasm_engine, WasmState};
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
use language::{LanguageServerName, LspAdapterDelegate};
|
use language::{LanguageServerName, LspAdapterDelegate};
|
||||||
use semantic_version::SemanticVersion;
|
use semantic_version::SemanticVersion;
|
||||||
|
use std::{ops::RangeInclusive, sync::Arc};
|
||||||
use wasmtime::{
|
use wasmtime::{
|
||||||
component::{Component, Instance, Linker, Resource},
|
component::{Component, Instance, Linker, Resource},
|
||||||
Store,
|
Store,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{wasm_engine, WasmState};
|
#[cfg(test)]
|
||||||
|
pub use latest::CodeLabelSpanLiteral;
|
||||||
use since_v0_0_6 as latest;
|
|
||||||
|
|
||||||
pub use latest::{
|
pub use latest::{
|
||||||
zed::extension::lsp::{Completion, CompletionKind, InsertTextFormat, Symbol, SymbolKind},
|
zed::extension::lsp::{Completion, CompletionKind, InsertTextFormat, Symbol, SymbolKind},
|
||||||
CodeLabel, CodeLabelSpan, Command, Range,
|
CodeLabel, CodeLabelSpan, Command, Range,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue