Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 33 additions & 5 deletions src/files.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
use relative_path::RelativePath;
use relative_path::RelativePathBuf;
use std::collections::BTreeMap;
use std::fs;
use std::io::Read;
use std::os::unix::fs::PermissionsExt;
use std::path::Path;

use crate::nix_file::NixFileStore;
use crate::problem::npv_145;
use crate::validation::ResultIteratorExt;
use crate::validation::Validation::Success;
use crate::{nix_file, ratchet, structure, validation};
Expand All @@ -13,9 +17,9 @@ pub fn check_files(
nixpkgs_path: &Path,
nix_file_store: &mut NixFileStore,
) -> validation::Result<BTreeMap<RelativePathBuf, ratchet::File>> {
process_nix_files(nixpkgs_path, nix_file_store, |_nix_file| {
// Noop for now, only boilerplate to make it easier to add future file-based checks
Ok(Success(ratchet::File {}))
process_nix_files(nixpkgs_path, nix_file_store, |relative_path, nix_file| {
let result = check_not_executable(relative_path, &nix_file.path)?;
Ok(result.map(|()| ratchet::File {}))
})
}

Expand All @@ -24,7 +28,7 @@ pub fn check_files(
fn process_nix_files(
nixpkgs_path: &Path,
nix_file_store: &mut NixFileStore,
f: impl Fn(&nix_file::NixFile) -> validation::Result<ratchet::File>,
f: impl Fn(&RelativePath, &nix_file::NixFile) -> validation::Result<ratchet::File>,
) -> validation::Result<BTreeMap<RelativePathBuf, ratchet::File>> {
// Get all Nix files
let files = {
Expand All @@ -38,7 +42,7 @@ fn process_nix_files(
.map(|path| {
// Get the (optionally-cached) parsed Nix file
let nix_file = nix_file_store.get(&path.to_path(nixpkgs_path))?;
let result = f(nix_file)?;
let result = f(&path, nix_file)?;
let val = result.map(|ratchet| (path, ratchet));
Ok::<_, anyhow::Error>(val)
})
Expand All @@ -50,6 +54,30 @@ fn process_nix_files(
}))
}

/// Check that a Nix file is not executable, unless it has a shebang (`#!`) line.
fn check_not_executable(
relative_path: &RelativePath,
absolute_path: &Path,
) -> validation::Result<()> {
let metadata = fs::metadata(absolute_path)?;
let mode = metadata.permissions().mode();

// If not executable, it's fine
if mode & 0o111 == 0 {
return Ok(Success(()));
}

// If executable, check for a shebang
let mut file = fs::File::open(absolute_path)?;
let mut buf = [0u8; 2];
let bytes_read = file.read(&mut buf)?;
if bytes_read >= 2 && buf == *b"#!" {
return Ok(Success(()));
}

Ok(npv_145::NixFileIsExecutableWithoutShebang::new(relative_path).into())
}

/// Recursively collects all Nix files in the relative `dir` within `base`
/// into the `files` `Vec`.
fn collect_nix_files(
Expand Down
4 changes: 4 additions & 0 deletions src/problem/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ pub mod npv_141;
pub mod npv_142;
pub mod npv_143;
pub mod npv_144;
pub mod npv_145;

pub mod npv_160;
pub mod npv_161;
Expand Down Expand Up @@ -108,6 +109,9 @@ pub enum Problem {
/// NPV-144: `package.nix` is not a file
PackageNixIsNotFile(npv_144::PackageNixIsNotFile),

/// NPV-145: Nix file is executable without shebang
NixFileIsExecutableWithoutShebang(npv_145::NixFileIsExecutableWithoutShebang),

/// NPV-160: top-level package moved out of by-name
TopLevelPackageMovedOutOfByName(npv_160::TopLevelPackageMovedOutOfByName),

Expand Down
20 changes: 20 additions & 0 deletions src/problem/npv_145.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
use std::fmt;

use derive_new::new;
use relative_path::RelativePathBuf;

#[derive(Clone, new)]
pub struct NixFileIsExecutableWithoutShebang {
#[new(into)]
relative_path: RelativePathBuf,
}

impl fmt::Display for NixFileIsExecutableWithoutShebang {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let Self { relative_path } = self;
write!(
f,
"- {relative_path}: Nix files must not be executable unless they have a shebang (`#!`) line.",
)
}
}
2 changes: 2 additions & 0 deletions tests/nix-file-executable/expected
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
- pkgs/by-name/fo/foo/package.nix: Nix files must not be executable unless they have a shebang (`#!`) line.
This PR introduces the problems listed above. Please fix them before merging, otherwise the base branch would break.
1 change: 1 addition & 0 deletions tests/nix-file-executable/main/default.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
import <test-nixpkgs> { root = ./.; }
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{ someDrv }: someDrv
Loading