protect against writes outside of output directory
This commit is contained in:
+49
-32
@@ -123,7 +123,37 @@ impl GitsyGenerator {
|
|||||||
Ok(file)
|
Ok(file)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_rendered(path: &str, rendered: &str) -> usize {
|
fn write_rendered(&self, path: &str, rendered: &str) -> usize {
|
||||||
|
// Ensure that the requested output path is actually a child
|
||||||
|
// of the output directory, as a sanity check to ensure we
|
||||||
|
// aren't writing out of bounds.
|
||||||
|
let canonical_root = self.settings.outputs.path.canonicalize().expect(&format!(
|
||||||
|
"Cannot find canonical version of output path: {}",
|
||||||
|
self.settings.outputs.path.display()
|
||||||
|
));
|
||||||
|
let canonical_path = PathBuf::from(path);
|
||||||
|
let has_relative_dirs = canonical_path.ancestors().any(|x| x.file_name().is_none() &&
|
||||||
|
x != Path::new("/"));
|
||||||
|
assert!(
|
||||||
|
canonical_path.is_absolute(),
|
||||||
|
"ERROR: write_rendered called with a relative path: {}",
|
||||||
|
path
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
!has_relative_dirs,
|
||||||
|
"ERROR: write_rendered called with a relative path: {}",
|
||||||
|
path
|
||||||
|
);
|
||||||
|
let _ = canonical_path
|
||||||
|
.ancestors()
|
||||||
|
.find(|x| x == &canonical_root)
|
||||||
|
.expect(&format!(
|
||||||
|
"Output file {} not contained in output path: {}",
|
||||||
|
canonical_path.display(),
|
||||||
|
canonical_root.display()
|
||||||
|
));
|
||||||
|
|
||||||
|
// Write the file to disk
|
||||||
let mut file = File::create(path).expect(&format!("Unable to write to output path: {}", path));
|
let mut file = File::create(path).expect(&format!("Unable to write to output path: {}", path));
|
||||||
file.write(rendered.as_bytes())
|
file.write(rendered.as_bytes())
|
||||||
.expect(&format!("Failed to save rendered html to path: {}", path));
|
.expect(&format!("Failed to save rendered html to path: {}", path));
|
||||||
@@ -305,10 +335,8 @@ impl GitsyGenerator {
|
|||||||
if let Some(templ_file) = self.settings.templates.repo_summary.as_deref() {
|
if let Some(templ_file) = self.settings.templates.repo_summary.as_deref() {
|
||||||
match tera.render(templ_file, &local_ctx) {
|
match tera.render(templ_file, &local_ctx) {
|
||||||
Ok(rendered) => {
|
Ok(rendered) => {
|
||||||
repo_bytes += GitsyGenerator::write_rendered(
|
repo_bytes +=
|
||||||
&self.settings.outputs.repo_summary(Some(&summary), None),
|
self.write_rendered(&self.settings.outputs.repo_summary(Some(&summary), None), &rendered);
|
||||||
&rendered,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
Err(x) => match x.kind {
|
Err(x) => match x.kind {
|
||||||
_ => error!("ERROR: {:?}", x),
|
_ => error!("ERROR: {:?}", x),
|
||||||
@@ -330,7 +358,7 @@ impl GitsyGenerator {
|
|||||||
paged_ctx.insert("branches", &page);
|
paged_ctx.insert("branches", &page);
|
||||||
match tera.render(templ_file, &paged_ctx) {
|
match tera.render(templ_file, &paged_ctx) {
|
||||||
Ok(rendered) => {
|
Ok(rendered) => {
|
||||||
repo_bytes += GitsyGenerator::write_rendered(&pagination.cur_page, &rendered);
|
repo_bytes += self.write_rendered(&pagination.cur_page, &rendered);
|
||||||
}
|
}
|
||||||
Err(x) => match x.kind {
|
Err(x) => match x.kind {
|
||||||
_ => error!("ERROR: {:?}", x),
|
_ => error!("ERROR: {:?}", x),
|
||||||
@@ -347,10 +375,8 @@ impl GitsyGenerator {
|
|||||||
if let Some(templ_file) = self.settings.templates.branch.as_deref() {
|
if let Some(templ_file) = self.settings.templates.branch.as_deref() {
|
||||||
match tera.render(templ_file, &local_ctx) {
|
match tera.render(templ_file, &local_ctx) {
|
||||||
Ok(rendered) => {
|
Ok(rendered) => {
|
||||||
repo_bytes += GitsyGenerator::write_rendered(
|
repo_bytes += self
|
||||||
&self.settings.outputs.branch(Some(&summary), Some(branch)),
|
.write_rendered(&self.settings.outputs.branch(Some(&summary), Some(branch)), &rendered);
|
||||||
&rendered,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
Err(x) => match x.kind {
|
Err(x) => match x.kind {
|
||||||
_ => error!("ERROR: {:?}", x),
|
_ => error!("ERROR: {:?}", x),
|
||||||
@@ -374,7 +400,7 @@ impl GitsyGenerator {
|
|||||||
paged_ctx.insert("tags", &page);
|
paged_ctx.insert("tags", &page);
|
||||||
match tera.render(templ_file, &paged_ctx) {
|
match tera.render(templ_file, &paged_ctx) {
|
||||||
Ok(rendered) => {
|
Ok(rendered) => {
|
||||||
repo_bytes += GitsyGenerator::write_rendered(&pagination.cur_page, &rendered);
|
repo_bytes += self.write_rendered(&pagination.cur_page, &rendered);
|
||||||
}
|
}
|
||||||
Err(x) => match x.kind {
|
Err(x) => match x.kind {
|
||||||
_ => error!("ERROR: {:?}", x),
|
_ => error!("ERROR: {:?}", x),
|
||||||
@@ -396,10 +422,8 @@ impl GitsyGenerator {
|
|||||||
if let Some(templ_file) = self.settings.templates.tag.as_deref() {
|
if let Some(templ_file) = self.settings.templates.tag.as_deref() {
|
||||||
match tera.render(templ_file, &local_ctx) {
|
match tera.render(templ_file, &local_ctx) {
|
||||||
Ok(rendered) => {
|
Ok(rendered) => {
|
||||||
repo_bytes += GitsyGenerator::write_rendered(
|
repo_bytes +=
|
||||||
&self.settings.outputs.tag(Some(&summary), Some(tag)),
|
self.write_rendered(&self.settings.outputs.tag(Some(&summary), Some(tag)), &rendered);
|
||||||
&rendered,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
Err(x) => match x.kind {
|
Err(x) => match x.kind {
|
||||||
_ => error!("ERROR: {:?}", x),
|
_ => error!("ERROR: {:?}", x),
|
||||||
@@ -424,7 +448,7 @@ impl GitsyGenerator {
|
|||||||
paged_ctx.insert("history", &page);
|
paged_ctx.insert("history", &page);
|
||||||
match tera.render(templ_file, &paged_ctx) {
|
match tera.render(templ_file, &paged_ctx) {
|
||||||
Ok(rendered) => {
|
Ok(rendered) => {
|
||||||
repo_bytes += GitsyGenerator::write_rendered(&pagination.cur_page, &rendered);
|
repo_bytes += self.write_rendered(&pagination.cur_page, &rendered);
|
||||||
}
|
}
|
||||||
Err(x) => match x.kind {
|
Err(x) => match x.kind {
|
||||||
_ => error!("ERROR: {:?}", x),
|
_ => error!("ERROR: {:?}", x),
|
||||||
@@ -443,10 +467,8 @@ impl GitsyGenerator {
|
|||||||
if let Some(templ_file) = self.settings.templates.commit.as_deref() {
|
if let Some(templ_file) = self.settings.templates.commit.as_deref() {
|
||||||
match tera.render(templ_file, &local_ctx) {
|
match tera.render(templ_file, &local_ctx) {
|
||||||
Ok(rendered) => {
|
Ok(rendered) => {
|
||||||
repo_bytes += GitsyGenerator::write_rendered(
|
repo_bytes += self
|
||||||
&self.settings.outputs.commit(Some(&summary), Some(commit)),
|
.write_rendered(&self.settings.outputs.commit(Some(&summary), Some(commit)), &rendered);
|
||||||
&rendered,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
Err(x) => match x.kind {
|
Err(x) => match x.kind {
|
||||||
_ => error!("ERROR: {:?}", x),
|
_ => error!("ERROR: {:?}", x),
|
||||||
@@ -470,10 +492,8 @@ impl GitsyGenerator {
|
|||||||
.expect("Invalid syntax highlighting theme specified.");
|
.expect("Invalid syntax highlighting theme specified.");
|
||||||
let css: String = css_for_theme_with_class_style(theme, syntect::html::ClassStyle::Spaced)
|
let css: String = css_for_theme_with_class_style(theme, syntect::html::ClassStyle::Spaced)
|
||||||
.expect("Invalid syntax highlighting theme specified.");
|
.expect("Invalid syntax highlighting theme specified.");
|
||||||
repo_bytes += GitsyGenerator::write_rendered(
|
repo_bytes +=
|
||||||
&self.settings.outputs.syntax_css(Some(&summary), None),
|
self.write_rendered(&self.settings.outputs.syntax_css(Some(&summary), None), css.as_str());
|
||||||
css.as_str(),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: parallelize the rest of the processing steps. This one is
|
// TODO: parallelize the rest of the processing steps. This one is
|
||||||
@@ -503,7 +523,7 @@ impl GitsyGenerator {
|
|||||||
if let Some(templ_file) = self.settings.templates.file.as_deref() {
|
if let Some(templ_file) = self.settings.templates.file.as_deref() {
|
||||||
match tera.render(templ_file, &local_ctx) {
|
match tera.render(templ_file, &local_ctx) {
|
||||||
Ok(rendered) => {
|
Ok(rendered) => {
|
||||||
local_bytes = GitsyGenerator::write_rendered(
|
local_bytes = self.write_rendered(
|
||||||
&self.settings.outputs.file(Some(&summary), Some(&file)),
|
&self.settings.outputs.file(Some(&summary), Some(&file)),
|
||||||
&rendered,
|
&rendered,
|
||||||
);
|
);
|
||||||
@@ -534,10 +554,8 @@ impl GitsyGenerator {
|
|||||||
if let Some(templ_file) = self.settings.templates.dir.as_deref() {
|
if let Some(templ_file) = self.settings.templates.dir.as_deref() {
|
||||||
match tera.render(templ_file, &local_ctx) {
|
match tera.render(templ_file, &local_ctx) {
|
||||||
Ok(rendered) => {
|
Ok(rendered) => {
|
||||||
repo_bytes += GitsyGenerator::write_rendered(
|
repo_bytes +=
|
||||||
&self.settings.outputs.dir(Some(&summary), Some(dir)),
|
self.write_rendered(&self.settings.outputs.dir(Some(&summary), Some(dir)), &rendered);
|
||||||
&rendered,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
Err(x) => match x.kind {
|
Err(x) => match x.kind {
|
||||||
_ => error!("ERROR: {:?}", x),
|
_ => error!("ERROR: {:?}", x),
|
||||||
@@ -612,8 +630,7 @@ impl GitsyGenerator {
|
|||||||
if let Some(templ_file) = self.settings.templates.repo_list.as_deref() {
|
if let Some(templ_file) = self.settings.templates.repo_list.as_deref() {
|
||||||
match tera.render(templ_file, &global_ctx) {
|
match tera.render(templ_file, &global_ctx) {
|
||||||
Ok(rendered) => {
|
Ok(rendered) => {
|
||||||
global_bytes +=
|
global_bytes += self.write_rendered(&self.settings.outputs.repo_list(None, None), &rendered);
|
||||||
GitsyGenerator::write_rendered(&self.settings.outputs.repo_list(None, None), &rendered);
|
|
||||||
}
|
}
|
||||||
Err(x) => match x.kind {
|
Err(x) => match x.kind {
|
||||||
_ => error!("ERROR: {:?}", x),
|
_ => error!("ERROR: {:?}", x),
|
||||||
@@ -624,7 +641,7 @@ impl GitsyGenerator {
|
|||||||
if let Some(templ_file) = self.settings.templates.error.as_deref() {
|
if let Some(templ_file) = self.settings.templates.error.as_deref() {
|
||||||
match tera.render(templ_file, &global_ctx) {
|
match tera.render(templ_file, &global_ctx) {
|
||||||
Ok(rendered) => {
|
Ok(rendered) => {
|
||||||
global_bytes += GitsyGenerator::write_rendered(&self.settings.outputs.error(None, None), &rendered);
|
global_bytes += self.write_rendered(&self.settings.outputs.error(None, None), &rendered);
|
||||||
}
|
}
|
||||||
Err(x) => match x.kind {
|
Err(x) => match x.kind {
|
||||||
_ => error!("ERROR: {:?}", x),
|
_ => error!("ERROR: {:?}", x),
|
||||||
|
|||||||
+2
-1
@@ -126,7 +126,8 @@ macro_rules! output_path_fn {
|
|||||||
_ => tmpl_str,
|
_ => tmpl_str,
|
||||||
};
|
};
|
||||||
let tmpl = PathBuf::from(tmpl_str);
|
let tmpl = PathBuf::from(tmpl_str);
|
||||||
let mut path = self.path.clone();
|
let mut path = self.path.clone().canonicalize()
|
||||||
|
.expect(&format!("ERROR: unable to canonicalize output path: {}", self.path.display()));
|
||||||
path.push(tmpl);
|
path.push(tmpl);
|
||||||
match $is_dir {
|
match $is_dir {
|
||||||
true => {
|
true => {
|
||||||
|
|||||||
Reference in New Issue
Block a user