hooks: forbid windows reserved names in the repos

Summary:
The root cause for S199754 was a file named "con.rs" was checked in onto the
repo. Since this is a reserved filename on Windows, this broke all Windows
users having it in their sparse profiles.

The rules for reserved names are defined as such by Microsoft:

"CON, PRN, AUX, NUL, COM1, COM2, COM3, COM4, COM5, COM6, COM7, COM8, COM9,
LPT1, LPT2, LPT3, LPT4, LPT5, LPT6, LPT7, LPT8, and LPT9. Also avoid these
names followed immediately by an extension; for example, NUL.txt is not
recommended. For more information, see Namespaces."

Of course, since the filesystem is case insensitive, these can have any casing.

Reviewed By: krallin

Differential Revision: D24453528

fbshipit-source-id: 389f15e2b1a88e3c1e8721fb7868616acabebc64
This commit is contained in:
Xavier Deguillard 2020-11-05 18:53:21 -08:00 committed by Facebook GitHub Bot
parent 62e5d16d62
commit c5480fdf6f
3 changed files with 245 additions and 0 deletions

View File

@ -20,6 +20,7 @@ mod lua_pattern;
pub(crate) mod no_bad_filenames;
mod no_insecure_filenames;
pub(crate) mod no_questionable_filenames;
pub(crate) mod no_windows_filenames;
use anyhow::Result;
use fbinit::FacebookInit;
@ -91,6 +92,11 @@ pub fn hook_name_to_file_hook(
.set_from_config(config)
.build()?,
)),
"no_windows_filenames" => Some(Box::new(
no_windows_filenames::NoWindowsFilenames::builder()
.set_from_config(config)
.build()?,
)),
_ => None,
})
}

View File

@ -0,0 +1,103 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This software may be used and distributed according to the terms of the
* GNU General Public License version 2.
*/
use crate::{CrossRepoPushSource, FileContentFetcher, FileHook, HookExecution, HookRejectionInfo};
use anyhow::{Context, Result};
use async_trait::async_trait;
use context::CoreContext;
use metaconfig_types::HookConfig;
use mononoke_types::{FileChange, MPath};
use regex::bytes::Regex;
#[derive(Default)]
pub struct NoWindowsFilenamesBuilder<'a> {
/// Paths on which bad Windows filenames are not disallowed.
allowed_paths: Option<&'a str>,
}
impl<'a> NoWindowsFilenamesBuilder<'a> {
pub fn set_from_config(mut self, config: &'a HookConfig) -> Self {
if let Some(v) = config.strings.get("allowed_paths") {
self = self.allowed_paths(v)
}
self
}
pub fn allowed_paths(mut self, regex: &'a str) -> Self {
self.allowed_paths = Some(regex);
self
}
pub fn build(self) -> Result<NoWindowsFilenames> {
Ok(NoWindowsFilenames {
allowed_paths: self
.allowed_paths
.map(Regex::new)
.transpose()
.context("Failed to create allowed_paths regex")?,
bad_windows_path_element: Regex::new(r"^(?i)(((com|lpt)\d$)|(con|prn|aux|nul))($|\.)")?,
})
}
}
pub struct NoWindowsFilenames {
allowed_paths: Option<Regex>,
bad_windows_path_element: Regex,
}
/// Hook to disallow bad Windows filenames from being pushed.
///
/// These bad filenames are described by Microsoft as:
/// "CON, PRN, AUX, NUL, COM1, COM2, COM3, COM4, COM5, COM6, COM7, COM8, COM9, LPT1, LPT2, LPT3,
/// LPT4, LPT5, LPT6, LPT7, LPT8, and LPT9. Also avoid these names followed immediately by an
/// extension; for example, NUL.txt is not recommended. For more information, see Namespaces."
impl NoWindowsFilenames {
pub fn builder<'a>() -> NoWindowsFilenamesBuilder<'a> {
NoWindowsFilenamesBuilder::default()
}
}
#[async_trait]
impl FileHook for NoWindowsFilenames {
async fn run<'this: 'change, 'ctx: 'this, 'change, 'fetcher: 'change, 'path: 'change>(
&'this self,
_ctx: &'ctx CoreContext,
_context_fetcher: &'fetcher dyn FileContentFetcher,
change: Option<&'change FileChange>,
path: &'path MPath,
cross_repo_push_source: CrossRepoPushSource,
) -> Result<HookExecution> {
if cross_repo_push_source == CrossRepoPushSource::PushRedirected {
// For push-redirected pushes we rely on the hook
// running in the original repo
return Ok(HookExecution::Accepted);
}
if change.is_none() {
return Ok(HookExecution::Accepted);
}
if let Some(allowed_paths) = &self.allowed_paths {
if allowed_paths.is_match(&path.to_vec()) {
return Ok(HookExecution::Accepted);
}
}
for element in path {
if self.bad_windows_path_element.is_match(element.as_ref()) {
return Ok(HookExecution::Rejected(HookRejectionInfo::new_long(
"Illegal windows filename",
format!("ABORT: Illegal windows filename: {}", element),
)));
}
}
Ok(HookExecution::Accepted)
}
}

View File

@ -0,0 +1,136 @@
# Copyright (c) Facebook, Inc. and its affiliates.
#
# This software may be used and distributed according to the terms of the
# GNU General Public License found in the LICENSE file in the root
# directory of this source tree.
$ . "${TEST_FIXTURES}/library.sh"
$ export LC_ALL=en_US.UTF-8 LANG=en_US.UTF-8 LANGUAGE=en_US.UTF-8
$ hook_test_setup no_windows_filenames <( \
> echo 'bypass_pushvar="ALLOW_BAD_WINDOWS_FILENAMES=true"'
> )
$ hg up -q 0
$ echo "ok" > "com"
$ hg ci -Aqm success
$ hgmn push -r . --to master_bookmark
pushing rev 2bdf0e02c487 to destination ssh://user@dummy/repo bookmark master_bookmark
searching for changes
adding changesets
adding manifests
adding file changes
added 1 changesets with 0 changes to 0 files
updating bookmark master_bookmark
$ hg up -q 0
$ echo "bad" > "COM5"
$ hg ci -Aqm failure
warning: filename contains 'COM5', which is reserved on Windows: COM5
$ hgmn push -r . --to master_bookmark
pushing rev 0a31cb8056d1 to destination ssh://user@dummy/repo bookmark master_bookmark
searching for changes
remote: Command failed
remote: Error:
remote: hooks failed:
remote: no_windows_filenames for 0a31cb8056d10d69d6652e754aeee9ecdd5f9e7b: ABORT: Illegal windows filename: COM5
remote:
remote: Root cause:
remote: hooks failed:
remote: no_windows_filenames for 0a31cb8056d10d69d6652e754aeee9ecdd5f9e7b: ABORT: Illegal windows filename: COM5
remote:
remote: Debug context:
remote: "hooks failed:\nno_windows_filenames for 0a31cb8056d10d69d6652e754aeee9ecdd5f9e7b: ABORT: Illegal windows filename: COM5"
abort: stream ended unexpectedly (got 0 bytes, expected 4)
[255]
$ hg up -q 0
$ echo "bad" > "nul.txt"
$ hg ci -Aqm failure
warning: filename contains 'nul', which is reserved on Windows: nul.txt
$ hgmn push -r . --to master_bookmark
pushing rev 7e7f8fb54a0b to destination ssh://user@dummy/repo bookmark master_bookmark
searching for changes
remote: Command failed
remote: Error:
remote: hooks failed:
remote: no_windows_filenames for 7e7f8fb54a0b8f692fbf224a33476b864f11dfe9: ABORT: Illegal windows filename: nul.txt
remote:
remote: Root cause:
remote: hooks failed:
remote: no_windows_filenames for 7e7f8fb54a0b8f692fbf224a33476b864f11dfe9: ABORT: Illegal windows filename: nul.txt
remote:
remote: Debug context:
remote: "hooks failed:\nno_windows_filenames for 7e7f8fb54a0b8f692fbf224a33476b864f11dfe9: ABORT: Illegal windows filename: nul.txt"
abort: stream ended unexpectedly (got 0 bytes, expected 4)
[255]
$ hg up -q 0
$ mkdir dir
$ echo "bad" > dir/CoN.txt
$ hg ci -Aqm failure
warning: filename contains 'CoN', which is reserved on Windows: dir/CoN.txt
$ hgmn push -r . --to master_bookmark
pushing rev 49604693a23c to destination ssh://user@dummy/repo bookmark master_bookmark
searching for changes
remote: Command failed
remote: Error:
remote: hooks failed:
remote: no_windows_filenames for 49604693a23c85a9ee0f6036d330b535842610dc: ABORT: Illegal windows filename: CoN.txt
remote:
remote: Root cause:
remote: hooks failed:
remote: no_windows_filenames for 49604693a23c85a9ee0f6036d330b535842610dc: ABORT: Illegal windows filename: CoN.txt
remote:
remote: Debug context:
remote: "hooks failed:\nno_windows_filenames for 49604693a23c85a9ee0f6036d330b535842610dc: ABORT: Illegal windows filename: CoN.txt"
abort: stream ended unexpectedly (got 0 bytes, expected 4)
[255]
$ hg up -q 0
$ mkdir dir
$ echo "ok" > dir/Icon.txt
$ hg ci -Aqm success
$ hgmn push -r . --to master_bookmark
pushing rev 74f01fef9e70 to destination ssh://user@dummy/repo bookmark master_bookmark
searching for changes
adding changesets
adding manifests
adding file changes
added 1 changesets with 0 changes to 0 files
updating bookmark master_bookmark
$ hg up -q 0
$ mkdir dir
$ echo "ok" > dir/Icom5
$ hg ci -Aqm success
$ hgmn push -r . --to master_bookmark
pushing rev 47222c857e63 to destination ssh://user@dummy/repo bookmark master_bookmark
searching for changes
adding changesets
adding manifests
adding file changes
added 1 changesets with 0 changes to 0 files
updating bookmark master_bookmark
$ hg up -q 0
$ mkdir con
$ echo "bad" > con/foo
$ hg ci -Aqm failure
warning: filename contains 'con', which is reserved on Windows: con/foo
$ hgmn push -r . --to master_bookmark
pushing rev 115c8cee8249 to destination ssh://user@dummy/repo bookmark master_bookmark
searching for changes
remote: Command failed
remote: Error:
remote: hooks failed:
remote: no_windows_filenames for 115c8cee824903baec5607a5b5d731f4a92e5859: ABORT: Illegal windows filename: con
remote:
remote: Root cause:
remote: hooks failed:
remote: no_windows_filenames for 115c8cee824903baec5607a5b5d731f4a92e5859: ABORT: Illegal windows filename: con
remote:
remote: Debug context:
remote: "hooks failed:\nno_windows_filenames for 115c8cee824903baec5607a5b5d731f4a92e5859: ABORT: Illegal windows filename: con"
abort: stream ended unexpectedly (got 0 bytes, expected 4)
[255]