commit: 08538feb54dedd434d95911f831dc783d0a9eb71
Author: Haelwenn (lanodan) Monnier <contact@hacktivis.me>
Date: Thu, 17 Feb 2022 20:20:01 +0100
Initial Commit
Diffstat:
A | .gitignore | 2 | ++ |
A | main.ha | 153 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
2 files changed, 155 insertions(+), 0 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -0,0 +1 @@
+/deblob
+\ No newline at end of file
diff --git a/main.ha b/main.ha
@@ -0,0 +1,153 @@
+// Copyright © 2019-2022 Haelwenn (lanodan) Monnier <contact+deblob-notice@hacktivis.me>
+// SPDX-License-Identifier: BSD-3-Clause
+
+use getopt;
+use fmt;
+use os;
+use fs;
+use io;
+use path;
+use bytes;
+use fnmatch;
+
+type invalid = !void;
+type ok = !void;
+
+let excludes: []str = [];
+let noop: bool = false;
+
+fn isBlob(filename: str) (bool|invalid) = {
+ static let buffer: [512]u8 = [0...];
+ static const elf: []u8 = [0x7F, 'E', 'L', 'F'];
+ static const ar: []u8 = ['!', '<', 'a', 'r', 'c', 'h', '>', '\n']; // <arch>!
+ static const bios: []u8 = [0x55, 0xAA];
+
+ let file = match(os::open(filename)) {
+ case let f: io::file => yield f;
+ case let e: fs::error =>
+ fmt::errorf("os::open({}): {}\n", filename, fs::strerror(e))!;
+ return invalid;
+ };
+ defer io::close(file);
+
+ const n = match(io::read(file, buffer)) {
+ case let s: size => yield s;
+ case io::EOF =>
+ fmt::errorf("EOF reading: {}\n", filename)!;
+ return invalid;
+ case let e: io::error =>
+ fmt::errorf("Error reading `{}`: {}\n", filename, io::strerror(e))!;
+ return invalid;
+ };
+
+ return bytes::hasprefix(buffer, elf) || bytes::hasprefix(buffer, ar) || bytes::hasprefix(buffer, bios);
+};
+
+fn is_excluded(filename: str) bool = {
+ for (let i = 0z; i < len(excludes); i += 1) {
+ if (fnmatch::fnmatch(excludes[i], filename, fnmatch::flags::NONE)) {
+ return true;
+ };
+ };
+
+ return false;
+};
+
+fn checkDir(dirname: str, ignoring: bool) (ok|invalid) = {
+ let dir_ls = match(os::readdir(dirname)) {
+ case let d: []fs::dirent => yield d;
+ case let e: fs::error =>
+ fmt::errorf("os::readdir({}): {}\n", dirname, fs::strerror(e))!;
+ return invalid;
+ };
+
+ for (let i = 0z; i < len(dir_ls); i += 1) {
+ let file = dir_ls[i];
+ let filename = path::join(dirname, file.name);
+
+ if(file.name == "." || file.name == "..") {
+ continue;
+ };
+
+ if(!ignoring) {
+ ignoring = is_excluded(filename);
+ };
+
+ if(fs::isdir(file.ftype)) {
+ match(checkDir(filename, ignoring)) {
+ case => continue;
+ };
+ } else {
+ let is_blob = match(isBlob(filename)) {
+ case let b: bool => yield b;
+ case invalid => continue;
+ };
+
+ if(!is_blob) {
+ continue;
+ };
+
+ if(ignoring) {
+ fmt::printf("ignoring: {}\n", filename)!;
+ continue;
+ };
+
+ if(noop) {
+ fmt::printf("detected: {}\n", filename)!;
+ continue;
+ };
+
+ fmt::printf("removing: {}\n", filename)!;
+
+ match(os::remove(filename)) {
+ case void => continue;
+ case let e: fs::error =>
+ fmt::errorf("os::remove({}): {}\n", filename, fs::strerror(e))!;
+ };
+ };
+ };
+
+ return ok;
+};
+
+fn fn_noop() void = {
+ return;
+};
+
+export fn main() void = {
+ let workdir = ".";
+
+ const cmd = getopt::parse(os::args,
+ "Remove binary executable files",
+ ('e', "NAME", "Exclude filename from removal (defaults to none)"),
+ ('d', "PATH", "Set working directory (default to current dir)"),
+ ('n', "No actual removal, only scan and log")
+ );
+ defer getopt::finish(&cmd);
+
+ for (let i = 0z; i < len(cmd.opts); i += 1) {
+ let opt = cmd.opts[i];
+ switch(opt.0) {
+ case 'e' => append(excludes, opt.1);
+ case 'd' => workdir = opt.1;
+ case 'n' => noop = true;
+ };
+ };
+
+ match(os::chdir(workdir)) {
+ case void => fn_noop;
+ case let e: fs::error =>
+ fmt::fatal("os::chdir({}): {}", workdir, fs::strerror(e));
+ };
+
+ fmt::println(":: Checking for blobs")!;
+
+ let ret = checkDir(".", false);
+
+ fmt::println(":: Done checking for blobs")!;
+
+ match(ret) {
+ case ok => os::exit(0);
+ case invalid => os::exit(1);
+ };
+};