///|
fn join_path(base : StringView, child : StringView) -> String {
if base.has_suffix("/") || base.has_suffix("\\") {
"\{base}\{child}"
} else {
"\{base}/\{child}"
}
}
///|
fn relative_path(root : StringView, path : StringView) -> String {
let normalized_root = root.replace_all(old="\\", new="/").trim_end(chars="/")
let normalized_path = path.replace_all(old="\\", new="/")
match normalized_path.strip_prefix(normalized_root) {
Some(rest) =>
match rest.strip_prefix("/") {
Some(relative) => relative.to_owned()
None => rest.to_owned()
}
None => normalized_path.to_owned()
}
}
///|
fn count_lines(text : StringView) -> Int {
if text.is_empty() {
return 0
}
let mut count = 0
for _ in text.split("\n") {
count += 1
}
count
}
///|
async fn find_manifest(root : String) -> (String, String) {
let current = join_path(root, "moon.mod")
if @fs.exists(current) {
return (current, @fs.read_file(current).text())
}
let legacy = join_path(root, "moon.mod.json")
if @fs.exists(legacy) {
return (legacy, @fs.read_file(legacy).text())
}
fail("MoonSeal could not find moon.mod or moon.mod.json in \{root}")
}
///|
async fn read_license(root : String) -> String? {
for name in ["LICENSE", "LICENSE.md", "LICENSE.txt"] {
let path = join_path(root, name)
if @fs.exists(path) {
return Some(@fs.read_file(path).text())
}
}
None
}
///|
fn excluded_directory(path : String) -> Bool {
let path = path.replace_all(old="\\", new="/")
path.has_suffix("/.git") ||
path.has_suffix("/_build") ||
path.has_suffix("/.mooncakes") ||
path.has_suffix("/node_modules")
}
///|
async fn collect_facts(root : String) -> @moonseal.ScanFacts {
let (manifest_path, manifest_text) = find_manifest(root)
let project = @moonseal.parse_manifest(manifest_path, manifest_text)
let files : Array[String] = []
let mut source_lines = 0
@fs.walk(root, max_concurrency=1, exclude=excluded_directory, async fn(
directory,
entries,
) {
for entry in entries {
let full_path = join_path(directory, entry)
let relative = relative_path(root, full_path)
files.push(relative)
if entry.has_suffix(".mbt") {
let content = @fs.read_file(full_path).text()
source_lines += count_lines(content)
}
}
})
{
project,
files,
moonbit_source_lines: source_lines,
license_text: read_license(root),
}
}
///|
fn usage() -> String {
let text =
#|MoonSeal 0.2.1 — MoonBit supply-chain audit and CycloneDX SBOM generator
#|
#|USAGE
#| moonseal scan [PATH] [--json | --markdown | --sarif]
#| moonseal check [PATH]
#| moonseal sbom [PATH] [-o FILE]
#| moonseal dependencies [PATH] [--json]
#| moonseal policy [PATH] [--policy FILE] [--json]
#| moonseal help
#|
#|COMMANDS
#| scan Audit a project; output text, JSON, or Markdown
#| check Run the audit and fail when blocking findings exist
#| sbom Generate a CycloneDX 1.6 software bill of materials
#| dependencies Analyze direct Mooncakes dependency metadata
#| policy Enforce repository-specific release requirements
#|
text
}
///|
fn positional_path(args : Array[String]) -> String {
let mut index = 0
while index < args.length() {
let arg = args[index]
if arg == "-o" || arg == "--output" || arg == "--policy" {
index += 2
continue
}
if !arg.has_prefix("-") {
return arg
}
index += 1
}
"."
}
///|
fn option_value(args : Array[String], names : Array[String]) -> String? {
let mut index = 0
while index < args.length() {
if names.any(fn(name) { args[index] == name }) && index + 1 < args.length() {
return Some(args[index + 1])
}
index += 1
}
None
}
///|
fn output_path(args : Array[String]) -> String? {
option_value(args, ["-o", "--output"])
}
///|
async fn run_scan(args : Array[String]) -> Unit {
let facts = collect_facts(positional_path(args))
let report = @moonseal.audit(facts)
if args.any(fn(arg) { arg == "--json" }) {
@stdio.stdout.write(@moonseal.render_json(report) + "\n")
} else if args.any(fn(arg) { arg == "--sarif" }) {
@stdio.stdout.write(@moonseal.render_sarif(report) + "\n")
} else if args.any(fn(arg) { arg == "--markdown" }) {
@stdio.stdout.write(@moonseal.render_markdown(report))
} else {
@stdio.stdout.write(@moonseal.render_text(report))
}
}
///|
async fn run_dependencies(args : Array[String]) -> Unit {
let facts = collect_facts(positional_path(args))
let health = @moonseal.analyze_dependencies(facts.project)
if args.any(fn(arg) { arg == "--json" }) {
@stdio.stdout.write(@moonseal.render_dependency_json(health) + "\n")
} else {
@stdio.stdout.write(@moonseal.render_dependency_text(health))
}
if !health.is_healthy() {
fail("MoonSeal found \{health.error_count()} blocking dependency issue(s)")
}
}
///|
async fn run_policy(args : Array[String]) -> Unit {
let root = positional_path(args)
let policy_path = match option_value(args, ["--policy"]) {
Some(path) => path
None => join_path(root, "moonseal-policy.json")
}
guard @fs.exists(policy_path) else {
fail("MoonSeal policy file does not exist: \{policy_path}")
}
let policy = @moonseal.parse_policy(@fs.read_file(policy_path).text())
let facts = collect_facts(root)
let report = @moonseal.audit(facts)
let result = @moonseal.evaluate_policy(policy, facts, report)
if args.any(fn(arg) { arg == "--json" }) {
@stdio.stdout.write(@moonseal.render_policy_json(result) + "\n")
} else {
@stdio.stdout.write(@moonseal.render_policy_text(result))
}
if !result.is_allowed() {
fail(
"MoonSeal policy blocked release with \{result.violations.length()} violation(s)",
)
}
}
///|
async fn run_check(args : Array[String]) -> Unit {
let facts = collect_facts(positional_path(args))
let report = @moonseal.audit(facts)
@stdio.stdout.write(@moonseal.render_text(report))
if !report.is_ready() {
fail(
"MoonSeal check failed with \{report.error_count()} blocking finding(s)",
)
}
}
///|
async fn run_sbom(args : Array[String]) -> Unit {
let facts = collect_facts(positional_path(args))
let output = @moonseal.cyclonedx_string(facts.project) + "\n"
match output_path(args) {
Some(path) => {
@fs.write_file(path, output, create_mode=CreateOrTruncate)
@stdio.stdout.write("Wrote CycloneDX 1.6 SBOM to \{path}\n")
}
None => @stdio.stdout.write(output)
}
}
///|
async fn main {
let argv = @env.args()
let command = if argv.length() >= 2 { argv[1] } else { "help" }
let args = if argv.length() > 2 { argv[2:].to_owned() } else { [] }
match command {
"scan" => run_scan(args)
"check" => run_check(args)
"sbom" => run_sbom(args)
"dependencies" | "deps" => run_dependencies(args)
"policy" => run_policy(args)
"help" | "--help" | "-h" => @stdio.stdout.write(usage())
"version" | "--version" | "-V" => @stdio.stdout.write("MoonSeal 0.2.1\n")
other => {
@stdio.stderr.write("Unknown command: \{other}\n\n")
@stdio.stderr.write(usage())
fail("unknown command")
}
}
}