modelos-secure 0.2.0 → 0.3.1
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.
- package/README.md +31 -12
- package/assets/login-CvPyhFJ7.js +1 -0
- package/assets/{login-DZgP66jr.js → login-DeAJGhiN.js} +22 -3
- package/cli.js +1763 -358
- package/hook/edge.mjs +6 -0
- package/hook/hook.mjs +4 -0
- package/package.json +2 -2
- package/python/modelos_secure/edge.py +19 -2
- package/python/modelos_secure/hook.py +3 -0
- package/assets/login-CipN1qFD.js +0 -1
package/README.md
CHANGED
|
@@ -6,18 +6,37 @@ Tests an AI system for Security, Safety and Quality. One command in the system's
|
|
|
6
6
|
npx modelos-secure scan .
|
|
7
7
|
```
|
|
8
8
|
|
|
9
|
-
It reads the code, draws the system's map and asks only what it cannot work out. Everything it decides goes into `modelos-secure.yaml`.
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
|
16
|
-
|
|
|
17
|
-
| `modelos-secure
|
|
18
|
-
| `modelos-secure
|
|
19
|
-
|
|
20
|
-
|
|
9
|
+
It reads the code the way an engineer would, starting where the app starts, draws the system's map and asks only what it cannot work out. Everything it decides goes into `modelos-secure.yaml`.
|
|
10
|
+
|
|
11
|
+
Before it sends anything it asks once whether a model may read the code it needs, or only an outline of it, or nothing. Every answer it sends passes a filter that takes secrets out, and settings files, keys and credentials are never read. `.modelos-secure/sent.jsonl` lists what was sent, `--explain` shows the outline and what may and may not be read without sending anything, and `--offline` keeps everything on your machine.
|
|
12
|
+
|
|
13
|
+
At a terminal, `modelos-secure` on its own starts a session about the app in the folder: ask about it in your own words, and each answer cites the files and lines it rests on, checked against the code.
|
|
14
|
+
|
|
15
|
+
| Command | Does |
|
|
16
|
+
| --------------------------------------------- | ------------------------------------------------------------------ |
|
|
17
|
+
| `modelos-secure` | At a terminal, a session about the app in this folder. |
|
|
18
|
+
| `modelos-secure login` | Saves a workspace token, and signs in with Google if needed. |
|
|
19
|
+
| `modelos-secure scan .` | Connects this repository. `--offline`, `--explain`, `--yes`. |
|
|
20
|
+
| `modelos-secure review` | Starts a review of this system. `--quick`, `--ci`, `--budget`. |
|
|
21
|
+
| `modelos-secure replay F-031` | Replays one finding. |
|
|
22
|
+
| `modelos-secure doctor` | Checks the setup and fixes what it can. |
|
|
23
|
+
| `modelos-secure runner` | Serves a review's attempts from this machine, in the app's folder. |
|
|
24
|
+
| `modelos-secure export R-1179 --format sarif` | Writes a review's findings as SARIF or JSON. |
|
|
25
|
+
| `modelos-secure --version` | Prints this CLI's version. |
|
|
26
|
+
|
|
27
|
+
A review of an app connected by the scan reaches it through a runner on the machine that runs it. `modelos-secure review` starts one itself, and keeps it while the review is paused; a review started from the app needs `modelos-secure runner` running in the app's folder, and says so if none is. A start command is checked before it runs and saved only once the app has started with it.
|
|
28
|
+
|
|
29
|
+
During a review, actions are held and never run. Outside a review the hooks do nothing. Requires Node 22 or newer. Python apps need Python 3.10 or newer.
|
|
30
|
+
|
|
31
|
+
Every command says what it takes with `--help`, such as `modelos-secure scan --help`, and refuses a flag it does not take.
|
|
32
|
+
|
|
33
|
+
| Exit code | Means |
|
|
34
|
+
| --------- | -------------------------------------------------------------- |
|
|
35
|
+
| 0 | Done. |
|
|
36
|
+
| 1 | Something failed, or a fix check found a break. |
|
|
37
|
+
| 2 | The command was used wrongly. |
|
|
38
|
+
| 3 | There are questions only a person can answer, as `--ci` lists. |
|
|
39
|
+
| 130 | Stopped with Ctrl-C. What was done is kept. |
|
|
21
40
|
|
|
22
41
|
## Signing in
|
|
23
42
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { t as loginCommand } from "./login-DeAJGhiN.js";
|
|
@@ -17,7 +17,6 @@ var grey = (t) => paint("90", t);
|
|
|
17
17
|
var bold = (t) => paint("1", t);
|
|
18
18
|
var interactive = () => Boolean(process.stdin.isTTY && process.stdout.isTTY) && !process.env.CI;
|
|
19
19
|
var pointer = unicode ? "›" : ">";
|
|
20
|
-
var dash = unicode ? "–" : "-";
|
|
21
20
|
function say(line = "") {
|
|
22
21
|
console.log(line);
|
|
23
22
|
}
|
|
@@ -36,6 +35,15 @@ function beforeStopping(work) {
|
|
|
36
35
|
async function finishBeforeStopping() {
|
|
37
36
|
await Promise.race([Promise.allSettled(onStop.map((work) => work())), new Promise((resolve) => setTimeout(resolve, 5e3))]);
|
|
38
37
|
}
|
|
38
|
+
/**
|
|
39
|
+
* One line that changes in place while work goes on, at a terminal only: what is being read now.
|
|
40
|
+
* An empty line clears it. Off a terminal it writes nothing, so a log holds only finished steps.
|
|
41
|
+
*/
|
|
42
|
+
function status(line) {
|
|
43
|
+
if (!process.stdout.isTTY) return;
|
|
44
|
+
const width = Math.max(20, (process.stdout.columns || 80) - 4);
|
|
45
|
+
process.stdout.write(`\r\u001b[2K${line ? ` ${grey(line.slice(0, width))}` : ""}`);
|
|
46
|
+
}
|
|
39
47
|
function step(name, detail, state = "done") {
|
|
40
48
|
if (state === "done") lastStep = name;
|
|
41
49
|
const right = state === "done" ? "done" : "";
|
|
@@ -120,7 +128,10 @@ async function askText(question, secret = false) {
|
|
|
120
128
|
resolve(value.trim());
|
|
121
129
|
return;
|
|
122
130
|
}
|
|
123
|
-
if (key.name === "backspace")
|
|
131
|
+
if (key.name === "backspace") {
|
|
132
|
+
if (value && !secret) process.stdout.write("\b \b");
|
|
133
|
+
value = value.slice(0, -1);
|
|
134
|
+
} else if (key.name === "tab") return;
|
|
124
135
|
else if (text && !key.ctrl) {
|
|
125
136
|
value += text;
|
|
126
137
|
if (!secret) process.stdout.write(text);
|
|
@@ -540,7 +551,15 @@ var IapRefusal = class extends ApiError {
|
|
|
540
551
|
this.kind = kind;
|
|
541
552
|
}
|
|
542
553
|
};
|
|
554
|
+
/** Hosts a token may reach over plain http: this machine only, as in local development. */
|
|
555
|
+
var LOCAL_HOSTS = /* @__PURE__ */ new Set([
|
|
556
|
+
"localhost",
|
|
557
|
+
"127.0.0.1",
|
|
558
|
+
"[::1]"
|
|
559
|
+
]);
|
|
543
560
|
function apiClient(connection, options = {}) {
|
|
561
|
+
const target = new URL(connection.url);
|
|
562
|
+
if (target.protocol === "http:" && !LOCAL_HOSTS.has(target.hostname)) throw new Error(`The address ${target.origin} starts with http://, so your token would travel in clear. Use its https:// address.`);
|
|
544
563
|
const proxy = proxyCredentials(connection.google, {
|
|
545
564
|
saved: (google) => saveRenewed(connection.url, google),
|
|
546
565
|
...options
|
|
@@ -658,4 +677,4 @@ async function loginCommand(given = {}) {
|
|
|
658
677
|
const who = connection.google?.email;
|
|
659
678
|
say(`Saved. This machine now works in ${me.workspace}${who ? `, signed in to Google as ${who}` : ""}.`);
|
|
660
679
|
}
|
|
661
|
-
export {
|
|
680
|
+
export { lastFinishedStep as _, apiClient as a, status as b, Interrupted as c, beforeStopping as d, blue as f, interactive as g, grey as h, IapRefusal as i, ask as l, finishBeforeStopping as m, parseLoginLine as n, atAddress as o, bold as p, ApiError as r, readConnection as s, loginCommand as t, askText as u, red as v, step as x, say as y };
|