ose-auditor 1.0.1 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/ose.js +171 -0
  2. package/package.json +5 -5
package/ose.js ADDED
@@ -0,0 +1,171 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * ose.js -- npm/npx wrapper for the OSE Auditor Python CLI.
4
+ *
5
+ * This script does NOT reimplement any audit logic. It is a thin launcher
6
+ * that:
7
+ * 1. Locates a usable Python 3 interpreter on the host machine.
8
+ * 2. Checks whether the `ose-auditor` PyPI package (which provides the
9
+ * `client.ose` module / `ose` console script) is already importable.
10
+ * 3. If not, installs it on first run via `pip install --user ose-auditor`
11
+ * (can be skipped with OSE_NO_AUTO_INSTALL=1, e.g. in CI images that
12
+ * pre-bake the dependency).
13
+ * 4. Forwards argv and the current environment (including OSE_API_KEY,
14
+ * OSE_SERVER_URL) straight through to `python -m client.ose`, and
15
+ * exits with the same exit code the Python process produced.
16
+ *
17
+ * Usage:
18
+ * npx ose-auditor audit ./my-project
19
+ * npx ose-auditor audit ./my-project --output report.json
20
+ * OSE_API_KEY=sk-... npx ose-auditor audit .
21
+ */
22
+
23
+ "use strict";
24
+
25
+ const { spawnSync } = require("child_process");
26
+ const os = require("os");
27
+
28
+ const MIN_PYTHON_MAJOR = 3;
29
+ const MIN_PYTHON_MINOR = 9;
30
+ const PYPI_PACKAGE = "ose-auditor";
31
+
32
+ /**
33
+ * Run a command synchronously, capturing stdout/stderr (not inherited),
34
+ * for internal probing only -- never used for the final audit invocation.
35
+ *
36
+ * @param {string} command
37
+ * @param {string[]} args
38
+ * @returns {{ok: boolean, stdout: string, stderr: string, status: number|null}}
39
+ */
40
+ function probe(command, args) {
41
+ const result = spawnSync(command, args, {
42
+ encoding: "utf8",
43
+ windowsHide: true,
44
+ });
45
+ if (result.error) {
46
+ return { ok: false, stdout: "", stderr: String(result.error), status: null };
47
+ }
48
+ return {
49
+ ok: result.status === 0,
50
+ stdout: (result.stdout || "").trim(),
51
+ stderr: (result.stderr || "").trim(),
52
+ status: result.status,
53
+ };
54
+ }
55
+
56
+ /**
57
+ * Find a usable Python 3 interpreter, preferring `python3` then `python`.
58
+ * Verifies the interpreter actually reports Python >= MIN_PYTHON_MAJOR.MIN_PYTHON_MINOR.
59
+ *
60
+ * @returns {string|null} The interpreter command name, or null if none found.
61
+ */
62
+ function findPython() {
63
+ const candidates = os.platform() === "win32" ? ["py", "python", "python3"] : ["python3", "python"];
64
+
65
+ for (const candidate of candidates) {
66
+ const versionCheck = probe(candidate, [
67
+ "-c",
68
+ "import sys; print('%d.%d' % sys.version_info[:2])",
69
+ ]);
70
+ if (!versionCheck.ok) continue;
71
+
72
+ const [majorStr, minorStr] = versionCheck.stdout.split(".");
73
+ const major = parseInt(majorStr, 10);
74
+ const minor = parseInt(minorStr, 10);
75
+ if (
76
+ major > MIN_PYTHON_MAJOR ||
77
+ (major === MIN_PYTHON_MAJOR && minor >= MIN_PYTHON_MINOR)
78
+ ) {
79
+ return candidate;
80
+ }
81
+ }
82
+ return null;
83
+ }
84
+
85
+ /**
86
+ * Check whether `client.ose` is importable with the given interpreter,
87
+ * i.e. whether the ose-auditor package is already installed.
88
+ *
89
+ * @param {string} python
90
+ * @returns {boolean}
91
+ */
92
+ function isOseInstalled(python) {
93
+ return probe(python, ["-c", "import client.ose"]).ok;
94
+ }
95
+
96
+ /**
97
+ * Install the ose-auditor package for the current user via pip.
98
+ *
99
+ * @param {string} python
100
+ * @returns {boolean} true on success
101
+ */
102
+ function installOse(python) {
103
+ process.stderr.write(
104
+ `[ose-auditor] First run: installing the '${PYPI_PACKAGE}' Python package via pip...\n`
105
+ );
106
+ const result = spawnSync(
107
+ python,
108
+ ["-m", "pip", "install", "--user", "--upgrade", PYPI_PACKAGE],
109
+ { stdio: "inherit", windowsHide: true }
110
+ );
111
+ if (result.status !== 0) {
112
+ process.stderr.write(
113
+ `[ose-auditor] pip install failed (exit code ${result.status}). ` +
114
+ `Try installing manually: ${python} -m pip install --user ${PYPI_PACKAGE}\n`
115
+ );
116
+ return false;
117
+ }
118
+ return true;
119
+ }
120
+
121
+ function main() {
122
+ const args = process.argv.slice(2);
123
+
124
+ const python = findPython();
125
+ if (!python) {
126
+ process.stderr.write(
127
+ `[ose-auditor] Could not find a Python ${MIN_PYTHON_MAJOR}.${MIN_PYTHON_MINOR}+ interpreter on PATH.\n` +
128
+ "Install Python from https://python.org and ensure 'python3' is on your PATH, then retry.\n"
129
+ );
130
+ process.exit(1);
131
+ }
132
+
133
+ if (!isOseInstalled(python)) {
134
+ if (process.env.OSE_NO_AUTO_INSTALL === "1") {
135
+ process.stderr.write(
136
+ `[ose-auditor] '${PYPI_PACKAGE}' is not installed and OSE_NO_AUTO_INSTALL=1 is set. ` +
137
+ `Install it manually: ${python} -m pip install ${PYPI_PACKAGE}\n`
138
+ );
139
+ process.exit(1);
140
+ }
141
+ const installed = installOse(python);
142
+ if (!installed || !isOseInstalled(python)) {
143
+ process.stderr.write(
144
+ "[ose-auditor] Installation did not succeed; cannot continue.\n"
145
+ );
146
+ process.exit(1);
147
+ }
148
+ }
149
+
150
+ if (!process.env.OSE_API_KEY) {
151
+ process.stderr.write(
152
+ "[ose-auditor] Note: OSE_API_KEY is not set. Audits with findings will need it " +
153
+ "to fetch patches (https://ose.crestsek.com). Audits with zero findings still work.\n"
154
+ );
155
+ }
156
+
157
+ const result = spawnSync(python, ["-m", "client.ose", ...args], {
158
+ stdio: "inherit",
159
+ env: process.env,
160
+ windowsHide: true,
161
+ });
162
+
163
+ if (result.error) {
164
+ process.stderr.write(`[ose-auditor] Failed to launch Python CLI: ${result.error}\n`);
165
+ process.exit(1);
166
+ }
167
+
168
+ process.exit(result.status === null ? 1 : result.status);
169
+ }
170
+
171
+ main();
package/package.json CHANGED
@@ -1,20 +1,20 @@
1
1
  {
2
2
  "name": "ose-auditor",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "description": "Run OSE Auditor (financial-logic security scanner for Node.js/TypeScript) without installing Python yourself -- this wrapper finds a Python 3 interpreter, installs the ose-auditor PyPI package on first run if needed, and forwards all arguments to it.",
5
5
  "license": "MIT",
6
6
  "bin": {
7
- "ose-audit": "bin/ose.js",
8
- "ose": "bin/ose.js"
7
+ "ose-audit": "ose.js",
8
+ "ose": "ose.js"
9
9
  },
10
10
  "files": [
11
- "bin/"
11
+ "ose.js"
12
12
  ],
13
13
  "engines": {
14
14
  "node": ">=16"
15
15
  },
16
16
  "scripts": {
17
- "test": "node bin/ose.js --version"
17
+ "test": "node ose.js --version"
18
18
  },
19
19
  "keywords": [
20
20
  "security",