Skip to content

Latest commit

 

History

History
382 lines (310 loc) · 21.3 KB

configuration.md

File metadata and controls

382 lines (310 loc) · 21.3 KB

Configuration and behavior

Options, defaults, precedence, inspection scope, exit codes and migration. CLI readers need only the sections up to Severity, reporters, CLI and exits; library and extension contracts start at Library use.

Rule settings

Save siro.config.json in the directory you pass to lint. It is automatically read as data, without executing repository code or requiring a local siro dependency:

{
  "exclude": ["test/fixtures", "vendor", "dist"],
  "rules": {
    "files-field": "warn",
    "provenance": "error",
    "store-server": "off"
  }
}

Disable an advisory only after reviewing your policy.

Rules run at their built-in severities unless overridden. Allowed values are 'error', 'warn', 'info', and 'off'. Off disables the check, not just its output. Unknown rule IDs are errors. Overrides apply to all selected directories; --severity separately controls display and failure thresholds.

Field Value and purpose
exclude Directory patterns; defaults to [].
installationRoots Paths or { path, pm?, pmVersion? } entries; defaults to ['.'].
rules Map of built-in or registered custom rule IDs to severity or 'off'.
pms Nonempty array restricting cwd's PM selection; does not force detection.
pmVersions Map such as { npm: '12.0.2' }; declares versions for cwd.
projectType 'application' or 'package'; overrides inference for all selected manifests.
customRules JS/TS only: custom rules; run only at cwd.
reporters JS/TS only: { name, format } reporters; can replace built-ins by name.

For TypeScript completion, first install siro in the project with npm install --save-dev --save-exact @pho9ubenaa/siro, then use siro.config.ts:

import { defineConfig } from '@pho9ubenaa/siro';
export default defineConfig({
  rules: { provenance: 'error' },
});

Run npx @pho9ubenaa/siro lint --config ./siro.config.ts to explicitly execute this trusted code. An npx temporary installation does not make imports from your repository's config resolvable. Without a local dependency, use JSON or an import-free .mjs file selected with --config.

Inspection scope: packages and installation roots

siro lint [cwd] discovers package.json and strict deno.json recursively, including cwd. PM workspace declarations and exclusions do not select packages or stop traversal. Use siro's exclude option to omit directories.

Each selected manifest receives applicable publication checks: files-field, npm-format publish-access, and manifest setting-availability checks. Generic checks run once per manifest, even with multiple PM targets. Private or unnamed packages are normally applications. Deno's name and publish: false are judged independently of npm metadata; parent privacy never propagates to children. An explicit projectType applies to all selected manifests, with API/CLI options overriding config.

Discovery does not inspect every package's installation policy. By default, only cwd receives installation checks. List other independently installed projects explicitly; siro does not infer them from lockfiles or PM declarations. For example, adapt these paths to existing directories in your repository:

{
  "exclude": ["test/fixtures", "vendor", "dist"],
  "installationRoots": [
    ".",
    "tools/standalone",
    { "path": "tools/no-detection-signal", "pm": "npm", "pmVersion": "12.0.2" }
  ]
}
npx @pho9ubenaa/siro lint . --installation-root . --installation-root tools/standalone
  • API arrays replace their config values. Repeated CLI --exclude or --installation-root values likewise replace the corresponding config array.
  • Naming a child does not automatically retain .. installationRoots: [] disables installation checks while keeping manifest checks; use config or API to supply an empty array or per-root PM/version objects.
  • Installation paths are literal, cwd-relative directories, not globs. They must match exact spelling, including case on all hosts. Missing, excluded, symlinked or always-skipped directories are rejected. Normalized duplicates run once; conflicting duplicate options are errors.
  • The . entry cannot contain PM options; use root API/CLI options or config pms / pmVersions instead.

Each installation root uses its own local settings, not inherited settings. A root without a manifest can receive installation checks if its PM is known. provenance runs only for publishable packages at installation roots: manifest-only children do not receive provenance-policy evaluation. Review the returned inspection record to see the selected inputs.

Common exclusions and filesystem behavior

Exclusions use one case-sensitive dialect on every OS:

  • Use /, *, ?, and whole-component **. Braces, character classes and extglob punctuation are literal. Backslash patterns are rejected.
  • test/fixtures and test/fixtures/** both exclude that directory and its entire subtree before files are read. exclude: ['**'] keeps only cwd.
  • cwd cannot be excluded with .. Leading ! reinclusion, absolute paths, parent traversal (..), NUL and patterns exceeding matcher limits are errors.
  • .git and node_modules directories are always skipped. Other directories, including dot directories, dist, vendor and fixtures, have no implicit exclusions.
  • .gitignore, tracked-file lists and PM workspace exclusions are not consulted.

Directory symlinks below cwd are not followed. cwd itself and file symlinks use normal native filesystem resolution; this is not a containment sandbox. Native filename spelling is preserved in results, including POSIX backslashes. See output paths before consuming them.

Selected malformed manifests, unsupported JSONC-only directories and read errors fail the entire scan. If both deno.json and deno.jsonc exist, strict deno.json takes precedence. Exclude intentionally broken fixtures. JSON/YAML roots must be objects; empty YAML is accepted, but empty JSON is invalid. Consumed manifest fields are type-checked even when publication checks are disabled; unknown fields are not whole-schema validated. Lockfile checks establish file presence, not git tracking, content validity or freshness.

Other PM configuration files are inspected only when needed by enabled checks. Missing optional PM configuration is treated as empty; missing lockfiles normally produce findings, not parse errors. Empty YAML/TOML/INI is accepted. This is not a whole-repository syntax audit. Parser diagnostics omit input excerpts; observed finding values are not redacted.

Strict filesystem and scan budgets

For a data-only inspection, use:

npx @pho9ubenaa/siro lint --no-config --strict-filesystem

--strict-filesystem (rejectSymlinks: true in the API) rejects a symlink selected as cwd and file/path symlinks below cwd, including dangling links. Symlinks above cwd are allowed; directory symlinks below cwd are not followed. The option applies to native data reads, including JSON siro settings, not executable config or extension code, and not to injected filesystems, which are trusted code. It is not an atomic containment guarantee; see the threat model for what still requires an isolated snapshot.

Each inspection has finite caller-controlled budgets. They are not config keys:

API limits key / CLI flag Default
maxFileBytes / --max-file-bytes 8 MiB (8388608)
maxTotalBytes / --max-total-bytes 64 MiB (67108864)
maxEntries / --max-entries 100000
maxDirectories / --max-directories 10000
maxDirectoryDepth / --max-directory-depth 128 (cwd depth 0)
maxConfigDepth / --max-config-depth 128 (mapping/array root depth 1)
maxFindings / --max-findings 50000 before severity filtering
maxOutputBytes / --max-output-bytes 32 MiB (33554432), including line endings

All overrides must be positive safe integers. Defaults are exported as DEFAULT_SCAN_LIMITS; API callers can provide a partial limits object. Budgets apply per scan, and cover input bytes and decoded UTF-8 text. Configuration depth also counts fields no check reads, so deep-but-irrelevant manifest nesting can still overflow. Injected filesystems must bound their own reads and allocations.

Input/evaluation overflow aborts with exit 2, without a partial success document. Output overflow is exit 70; JSON fails instead of emitting a partial document, while GitHub output may already contain annotations. No findings are silently truncated. Direct built-in reporter calls use defaults, or supplied context.limits. Limits do not sandbox trusted extensions or impose a hard CPU timeout. Isolate synchronous API work in a subprocess/container when hard time/memory bounds are needed.

When aggregate budgets are exceeded, reduce the scan scope; scan independent projects separately where possible. Changing the target directory can change configuration and root-level checks, so splitting a shared workspace is not equivalent to checking it as a whole. Individual file-size and configuration-depth overflows instead require smaller files or simpler nesting.

Do not modify inputs during a scan; a consistent filesystem snapshot is not guaranteed.

Target PM versions

Targets describe your intended policy; siro does not run or download a PM binary.

Evaluation directory PM selection Version precedence
cwd API pm / --pm, otherwise local detection; config pms restricts selection API pmVersion / --pm-version → config pmVersions[pm] → local exact packageManager
Additional installation root Entry pm, otherwise local detection Entry pmVersion → local exact packageManager
Other discovered package Its own manifest declaration; Deno format identifies Deno Its own exact stable declaration only

Manifest targets are filtered for their format: Deno for deno.json, other PMs for package.json. Local detection combines packageManager, lockfiles and manager-specific configuration presence. .npmrc alone identifies no PM. pms restricts selection rather than forcing managers; version maps do not select managers. Root options never propagate to additional roots or child manifests.

A publication-only package without a known PM still receives generic checks. An empty target list or a target without a version means availability was not assessed, not that its settings are safe. Installation roots and active custom rules require a detectable or explicit PM. Without installation or active custom checks, cwd's target can be unknown. Root PM options remain usable with installationRoots: [] without re-enabling installation checks.

Explicit versions must be exact stable SemVer; pmVersion requires pm. Build metadata, including Corepack hashes, is accepted. Ranges, prereleases, tags, partial versions and v prefixes are rejected in explicit options/config. Such manifest declarations leave the version unknown while retaining name detection where possible.

unsupported-settings checks only the listed setting/file pairs. Unknown targets, unlisted settings, later removals, backports and version-specific value grammars are outside this rule. Known too-old targets receive manual upgrade guidance rather than unsupported automatic remedies.

Local policy, not effective runtime configuration

Checks use local files, not environment variables, user/global configuration, install/publish commands or workspace inheritance. PM-specific precedence and exceptions are described in policy and sources, including npm provenance, Deno release-age fallback and npm shrinkwrap compatibility.

JSON CLI configuration

The CLI automatically reads only cwd's siro.config.json, not parent, child or additional-root siro settings. JSON takes precedence over coexisting executable config, which remains unexecuted. Invalid JSON settings fail with exit 2; there is no fallback to another configuration.

Use strict JSON without comments. Only the data fields above and built-in rule IDs are accepted. customRules, reporters, module references and config inheritance are not supported in JSON, even when selected explicitly with --config <path>. An explicitly selected file replaces automatic configuration; files are not merged.

JSON reads share the CLI's file/total-byte budgets with inspection and honor --max-config-depth and --strict-filesystem. In strict mode, an explicitly selected JSON file must be inside the lint target. --no-config skips JSON settings too. Data-only settings can still disable checks or exclude inputs; do not trust an unreviewed configuration as your security policy.

Executable CLI configuration

Executable .ts, .mjs, and .js settings require --config <path>. Paths are relative to the shell's working directory, not the lint target. Only the selected file is loaded; settings are not merged with other files. Config runs with the caller's privileges; see the threat model. TypeScript must use Node-supported erasable syntax. This option is trust, not a sandbox.

When JSON is absent and --config is omitted, finding cwd's siro.config.ts, .mjs, or .js produces a migration error (exit 2) without executing it. Move data settings to siro.config.json, or explicitly select trusted code. Parents and child/additional-root configs are not loaded. --no-config skips probing and loading all repository configuration, ignoring its settings and extensions. It cannot be combined with --config. CLI options and built-in defaults still apply; unknown custom reporter names are errors.

Export a config object synchronously, using object literals for configuration and setting maps. Unknown keys, unknown or duplicate rule IDs, and malformed extensions are errors. All selected targets share cwd's rule overrides and reporter registration.

Library use

Install siro as a project dependency before importing it. lint is synchronous; lintCommand evaluates and reports asynchronously. Neither implicitly loads repository configuration. loadConfig(cwd) reads JSON automatically, but refuses existing executable config if JSON is absent. To execute trusted configuration, provide loadConfig(cwd, { configPath: 'siro.config.mjs' }); API paths are relative to cwd. Changes to imported modules may require restarting the process. loadConfig accepts caller-controlled limits for JSON reads and nesting; separate API calls to load and lint have separate budgets.

This example assumes tools/standalone is an existing independent install project:

import { asAbsPath, lint, lintCommand, loadConfig, nodeIO } from '@pho9ubenaa/siro';
const cwd = asAbsPath(process.cwd());
const config = await loadConfig(cwd); // Reads JSON settings, never auto-executes code.
const options = {
  cwd,
  config,
  exclude: ['test/fixtures'],
  installationRoots: ['.', 'tools/standalone'],
};
const result = lint(options); // Returns findings without reporting.
const exitCode = await lintCommand({ ...options, reporter: 'json' }, nodeIO); // Evaluates again.

Custom checks and reporters belong in config.customRules and config.reporters. Use the public @pho9ubenaa/siro entry point; internal modules are not supported APIs.

Injected filesystems

The optional fs implements the exported FileSystem interface:

  • readDirectories(directory) is required. Return an array of immediate child directory names, not paths, and omit symlinked directories. There is no fallback to the host filesystem.
  • readText(path) returns text or undefined for absence; exists(path) returns whether a regular file exists. File symlinks are accepted.
  • Only ENOENT means absence. Access errors and non-file entries must throw.

Custom checks and remediation

Use defineRule for PM-specific checks or requireConfigKey for a scalar setting. A check receives RuleContext, including optional pmVersion and readConfig(file). Return ok, na, a violation, or a nonempty violations group. Violations contain message and optional file, severity, actual, expected, and remediation. Invalid or async results are configuration errors. Rules run only at cwd.

Rule file paths are context-relative; reported file and operation paths are cwd-relative. A missing violation file uses the binding's file, if any. See JSON output for remediation shapes and path handling.

Use supported PM names and an object literal for requireConfigKey.bindings. Its optional accept and applies functions must return a boolean synchronously. Invalid names or return values are configuration errors.

For requireConfigKey, documentedDefault may reduce omitted-setting severity only with defaultSafety: 'unconditional' and a default satisfying the requirement. Omitted safety or 'conditional' keeps configured severity. defaultSatisfiedSeverity is 'info' unless specified, and may be 'off'. A severity override cannot restore a finding the check never emitted. VersionNote is descriptive, not a policy switch.

Severity, reporters, CLI and exits

User rule overrides take precedence over result, binding and rule severity. By default all findings are displayed but only errors fail. --severity changes both thresholds. Summary counts displayed findings; filtering does not change inspection. A reporter cannot change the already computed findings exit code, but reporting failures reject the command. For example, enforce-strict-ssl is warn-level by default: use --severity warn to make detected TLS policy violations fail CI. Rule severities and command thresholds are not vulnerability severity ratings.

Built-in reporters are pretty, json and github; registered reporters may replace them by name. Reporters receive format(result, io, { cwd }) and must be awaited, including direct calls. GitHub annotation files are absolute paths based on this scan cwd; API/JSON paths remain cwd-relative.

IO.stdout / stderr may return promises. Await direct writes and handle rejection. Reporters must finish their writes before returning. lintCommand waits for reporting to finish; output failure rejects the API and exits the CLI with 70, not the findings exit 1.

check aliases lint. Value options must be nonempty; only --exclude and --installation-root repeat. Boolean flags take no values. --no-config is an explicit option, not support for arbitrary --no- variants; it and --strict-filesystem cannot be repeated. Help takes priority over version, and version over linting. Arguments after -- are rejected. Run npx @pho9ubenaa/siro lint --help for CLI syntax.

Exit Meaning
0 No findings meet the failure threshold
1 Findings meet the threshold
2 Invalid options/config/results or filesystem access; inspection incomplete
70 Unexpected exception, including output, custom-rule or reporter failure

Migration from 0.5.x

  • Remove --workspaces / workspaces, even workspaces: false. Discovery is now recursive; PM exclusions no longer hide fixtures or vendor/dist packages. Add siro exclude patterns; ['**'] keeps only cwd.
  • Add independent installationRoots deliberately; discovery does not add them.
  • Stop relying on root PM/version inheritance or duplicated generic PM findings.
  • Consume schema 3: optional finding PM, required directory and inspection scope; all finding and operation paths are cwd-relative.
  • Injected filesystems must implement readDirectories; resolveDirectory is removed.
  • Custom requireConfigKey defaults without defaultSafety are conservative.
  • Direct reporter calls require context and awaiting: await githubReporter.format(result, io, { cwd }). Two-argument custom implementations may ignore context, but callers must supply it.
  • IO.stdout / stderr, including nodeIO, may now return promises. Await direct writes and handle rejection. Finish reporter writes before returning.