modelos-secure 0.3.1 → 0.3.2
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/cli.js +54 -38
- package/package.json +1 -1
package/cli.js
CHANGED
|
@@ -23,7 +23,7 @@ if (existsSync(resolve(here, "wasm"))) {
|
|
|
23
23
|
}
|
|
24
24
|
// package.json
|
|
25
25
|
var name = "modelos-secure";
|
|
26
|
-
var version$1 = "0.3.
|
|
26
|
+
var version$1 = "0.3.2";
|
|
27
27
|
var type = "module";
|
|
28
28
|
var bin = { "modelos-secure": "dist/cli.js" };
|
|
29
29
|
var exports$1 = { "./hook": "./dist/hook/hook.mjs" };
|
|
@@ -21996,21 +21996,23 @@ function read(result, understood) {
|
|
|
21996
21996
|
facts: mergeFacts(result.facts, verified.facts).filter((f) => !(f.kind === "way-in" && f.adapter !== "model" && f.function && acts.has(f.function)))
|
|
21997
21997
|
};
|
|
21998
21998
|
}
|
|
21999
|
-
// src/
|
|
22000
|
-
var TOOL_POWERS = [
|
|
22001
|
-
"read",
|
|
22002
|
-
"read private data",
|
|
22003
|
-
"change",
|
|
22004
|
-
"send outside",
|
|
22005
|
-
"move money",
|
|
22006
|
-
"delete"
|
|
22007
|
-
];
|
|
22008
|
-
var CONSENT_TEXT = "To understand your app, the scan lets a model read your code the way an engineer would, on our own Google Cloud project. It starts from an outline: file paths, the names and signatures of functions and classes, your prompts and the names of your settings. Then it asks for the files and lines it needs, and only those are sent, each through a filter that takes secrets out. It never sends settings files, keys, certificates or credential files, the values of your settings, or files over 1 MB. Every answer it sends is listed in .modelos-secure/sent.jsonl. Run with --explain to see what may be read, or --offline to keep everything here.";
|
|
21999
|
+
// src/several-apps.ts
|
|
22009
22000
|
var AI_DEPENDENCIES = /"(ai|openai|@google\/genai|langchain|@langchain\/[\w-]+|@anthropic-ai\/sdk|@mastra\/core|@openai\/agents)"|^\s*"?(openai|google-genai|langgraph|langchain|anthropic|crewai|pydantic-ai|smolagents|openai-agents|llama-index|autogen-agentchat|litellm)\b/m;
|
|
22010
|
-
/**
|
|
22001
|
+
/** Whether a file imports an AI library. */
|
|
22011
22002
|
var AI_IMPORT = /^\s*(?:import|from)\b[^\n]*\b(openai|anthropic|genai|gemini|langchain|langgraph|llama_index|crewai|pydantic_ai|smolagents|autogen|mastra|ai-sdk|agents|vertexai|litellm|ollama)\b|require\(["'](openai|ai|@anthropic-ai\/sdk)["']\)/m;
|
|
22012
|
-
var
|
|
22013
|
-
|
|
22003
|
+
var MANIFESTS = [
|
|
22004
|
+
"package.json",
|
|
22005
|
+
"pyproject.toml",
|
|
22006
|
+
"requirements.txt"
|
|
22007
|
+
];
|
|
22008
|
+
async function dependsOnAi(folder) {
|
|
22009
|
+
for (const manifest of MANIFESTS) {
|
|
22010
|
+
const text = await readFile(join(folder, manifest), "utf8").catch(() => "");
|
|
22011
|
+
if (AI_DEPENDENCIES.test(text)) return true;
|
|
22012
|
+
}
|
|
22013
|
+
return false;
|
|
22014
|
+
}
|
|
22015
|
+
/** Folders below the root that are apps of their own. What is inside an app is part of it. */
|
|
22014
22016
|
async function appsIn(root) {
|
|
22015
22017
|
const found = [];
|
|
22016
22018
|
const visit = async (dir, depth) => {
|
|
@@ -22023,26 +22025,34 @@ async function appsIn(root) {
|
|
|
22023
22025
|
"venv"
|
|
22024
22026
|
].includes(entry.name)) continue;
|
|
22025
22027
|
const full = join(dir, entry.name);
|
|
22026
|
-
|
|
22027
|
-
|
|
22028
|
-
"pyproject.toml",
|
|
22029
|
-
"requirements.txt"
|
|
22030
|
-
]) {
|
|
22031
|
-
const text = await readFile(join(full, manifest), "utf8").catch(() => "");
|
|
22032
|
-
if (AI_DEPENDENCIES.test(text)) {
|
|
22033
|
-
found.push(inRepo(root, full));
|
|
22034
|
-
break;
|
|
22035
|
-
}
|
|
22036
|
-
}
|
|
22037
|
-
await visit(full, depth + 1);
|
|
22028
|
+
if (await dependsOnAi(full)) found.push(inRepo(root, full));
|
|
22029
|
+
else await visit(full, depth + 1);
|
|
22038
22030
|
}
|
|
22039
22031
|
};
|
|
22040
22032
|
await visit(root, 1);
|
|
22041
22033
|
return found;
|
|
22042
22034
|
}
|
|
22043
|
-
|
|
22035
|
+
/** Whether any code here, wherever it sits, builds on an AI library. */
|
|
22036
|
+
function usesAi(result) {
|
|
22044
22037
|
return result.facts.some((f) => f.kind === "tool" || f.kind === "agent") || result.project.files.some((f) => AI_IMPORT.test(f.text));
|
|
22045
22038
|
}
|
|
22039
|
+
/** Whether the folder is an app itself, beside any apps in its subfolders. */
|
|
22040
|
+
async function rootIsAnApp(root, result, apps) {
|
|
22041
|
+
if (await dependsOnAi(root)) return true;
|
|
22042
|
+
const outside = (path) => !apps.some((app) => path === app || path.startsWith(`${app}/`));
|
|
22043
|
+
return result.facts.some((f) => (f.kind === "tool" || f.kind === "agent") && outside(f.file)) || result.project.files.some((f) => outside(f.path) && AI_IMPORT.test(f.text));
|
|
22044
|
+
}
|
|
22045
|
+
// src/commands/scan.ts
|
|
22046
|
+
var TOOL_POWERS = [
|
|
22047
|
+
"read",
|
|
22048
|
+
"read private data",
|
|
22049
|
+
"change",
|
|
22050
|
+
"send outside",
|
|
22051
|
+
"move money",
|
|
22052
|
+
"delete"
|
|
22053
|
+
];
|
|
22054
|
+
var CONSENT_TEXT = "To understand your app, the scan lets a model read your code the way an engineer would, on our own Google Cloud project. It starts from an outline: file paths, the names and signatures of functions and classes, your prompts and the names of your settings. Then it asks for the files and lines it needs, and only those are sent, each through a filter that takes secrets out. It never sends settings files, keys, certificates or credential files, the values of your settings, or files over 1 MB. Every answer it sends is listed in .modelos-secure/sent.jsonl. Run with --explain to see what may be read, or --offline to keep everything here.";
|
|
22055
|
+
var plural = (n, one, many = `${one}s`) => `${n.toLocaleString("en-GB")} ${n === 1 ? one : many}`;
|
|
22046
22056
|
async function scanCommand(dir, options) {
|
|
22047
22057
|
const retry = {
|
|
22048
22058
|
yes: options.yes,
|
|
@@ -22062,21 +22072,27 @@ async function scanCommand(dir, options) {
|
|
|
22062
22072
|
const result = await scanFolder(dir);
|
|
22063
22073
|
say(`${bold("ModelOS Secure")} scanning ${result.look.name}\n`);
|
|
22064
22074
|
step("Look", `${[...result.look.languages, ...result.look.frameworks].join(", ") || "No source found"}, ${plural(result.look.fileCount, "file")}`);
|
|
22065
|
-
|
|
22066
|
-
|
|
22067
|
-
|
|
22068
|
-
|
|
22075
|
+
const apps = await appsIn(dir);
|
|
22076
|
+
if (apps.length > 1 && !await rootIsAnApp(dir, result, apps)) {
|
|
22077
|
+
const several = `This repository has ${apps.length.toString()} apps with model calls.`;
|
|
22078
|
+
if (options.yes) say(`\n ${several} --yes takes the first, ${apps[0]}.\n`);
|
|
22079
|
+
const chosen = options.yes ? [apps[0]] : await ask(several, void 0, [
|
|
22080
|
+
...apps.map((app) => ({
|
|
22069
22081
|
label: `Pick ${app}`,
|
|
22070
22082
|
value: [app]
|
|
22071
|
-
})),
|
|
22083
|
+
})),
|
|
22084
|
+
{
|
|
22072
22085
|
label: `Scan all ${apps.length.toString()} as separate systems`,
|
|
22073
22086
|
value: apps
|
|
22074
|
-
}
|
|
22075
|
-
|
|
22076
|
-
|
|
22077
|
-
|
|
22078
|
-
|
|
22079
|
-
|
|
22087
|
+
},
|
|
22088
|
+
{
|
|
22089
|
+
label: "They are one system: scan this whole folder",
|
|
22090
|
+
value: []
|
|
22091
|
+
}
|
|
22092
|
+
]);
|
|
22093
|
+
for (const app of chosen) await scanCommand(join(dir, app), options);
|
|
22094
|
+
if (chosen.length > 0) return;
|
|
22095
|
+
} else if (apps.length === 1 && !usesAi(result)) return scanCommand(join(dir, apps[0]), options);
|
|
22080
22096
|
step("Read", `${plural(result.project.files.length, "file")} parsed${result.adapters.some((a) => a !== "http") ? `, with the reader for ${result.adapters.filter((a) => a !== "http").join(" and ")}` : ""}`);
|
|
22081
22097
|
const outline = await buildOutline(result.look, result.facts, result.project);
|
|
22082
22098
|
if (options.explain) {
|