atris 3.82.5 → 3.82.7

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.
@@ -0,0 +1,108 @@
1
+ 'use strict';
2
+
3
+ // The schema for lib/sentinel-rules.json, the literal rules of the guard and
4
+ // the agent check. `atris sentinel improve` may change only that file, so
5
+ // this is the wall between a proposal and the code that judges it:
6
+ //
7
+ // known keys only, at every level. A missing or extra key is an error
8
+ // strings and lists of strings only. No functions, no objects where a
9
+ // list belongs. No `pattern` field anywhere: proposals may add or
10
+ // remove literal entries, never a regular expression
11
+ // every entry is a short literal: a host name, a program name, a secret
12
+ // word, an option name or a command substring. The guard matches them
13
+ // with plain string operations. There is no pattern to compile and
14
+ // nothing that can stall on long input
15
+ //
16
+ // validateRules() returns a list of plain problems. Empty means valid.
17
+
18
+ const MAX_LIST = 300;
19
+ const MAX_ITEM = 200;
20
+
21
+ const GUARD_KEYS = ['allow_hosts', 'net_tools', 'secret_parts', 'safe_net', 'safe_net_aliases', 'read_only', 'read_only_git', 'db_clients', 'command_blocks'];
22
+ const AGENT_KEYS = ['override_substrings', 'secret_env_names'];
23
+ const SAFE_NET_KEYS = ['flags', 'values', 'local_values', 'short_flags', 'short_values', 'items'];
24
+ const BLOCK_KEYS = ['id', 'category', 'all', 'programs'];
25
+
26
+ const isObj = (v) => v !== null && typeof v === 'object' && !Array.isArray(v);
27
+
28
+ // A proposal may never carry a regex. `pattern` is the field name the old
29
+ // shape used. It is called out by name wherever it appears.
30
+ function noPatternField(obj, where, problems) {
31
+ if (isObj(obj) && 'pattern' in obj) problems.push(`${where} has a pattern field. proposals may add literal entries only, never a regular expression`);
32
+ }
33
+
34
+ function exactKeys(obj, allowed, where, problems, optional = []) {
35
+ for (const k of Object.keys(obj)) if (!allowed.includes(k)) problems.push(`${where} has an unknown key: ${k}`);
36
+ for (const k of allowed) if (!optional.includes(k) && !(k in obj)) problems.push(`${where} is missing ${k}`);
37
+ }
38
+
39
+ function strList(v, where, problems, shape = null, maxItem = MAX_ITEM) {
40
+ if (!Array.isArray(v)) { problems.push(`${where} must be a list`); return; }
41
+ if (v.length > MAX_LIST) problems.push(`${where} has more than ${MAX_LIST} items`);
42
+ v.forEach((x, i) => {
43
+ if (typeof x !== 'string') problems.push(`${where}[${i}] must be a string`);
44
+ else if (!x.length) problems.push(`${where}[${i}] is empty`);
45
+ else if (x.length > maxItem) problems.push(`${where}[${i}] is longer than ${maxItem} characters`);
46
+ else if (shape && !shape.test(x)) problems.push(`${where}[${i}] has an odd shape: ${x.slice(0, 40)}`);
47
+ });
48
+ }
49
+
50
+ function validateRules(rules) {
51
+ const problems = [];
52
+ if (!isObj(rules)) return ['the rules file must hold one object'];
53
+ exactKeys(rules, ['version', 'guard', 'agents'], 'the rules file', problems);
54
+ if (rules.version !== 2) problems.push('version must be 2');
55
+ const g = rules.guard;
56
+ if (!isObj(g)) problems.push('guard must be an object');
57
+ else {
58
+ noPatternField(g, 'guard', problems);
59
+ exactKeys(g, GUARD_KEYS, 'guard', problems);
60
+ strList(g.allow_hosts, 'guard.allow_hosts', problems, /^[a-z0-9.:_-]{1,100}$/i);
61
+ for (const k of ['net_tools', 'read_only', 'db_clients']) strList(g[k], `guard.${k}`, problems, /^[A-Za-z0-9._+[-]{1,40}$/);
62
+ strList(g.read_only_git, 'guard.read_only_git', problems, /^[a-z-]{1,30}$/);
63
+ strList(g.secret_parts, 'guard.secret_parts', problems, /^[A-Z0-9]{1,30}$/);
64
+ if (!isObj(g.safe_net)) problems.push('guard.safe_net must be an object');
65
+ else {
66
+ for (const [prog, spec] of Object.entries(g.safe_net)) {
67
+ const where = `guard.safe_net.${prog}`;
68
+ if (!/^[a-z]{1,20}$/.test(prog)) problems.push(`${where} is not a program name`);
69
+ if (!isObj(spec)) { problems.push(`${where} must be an object`); continue; }
70
+ noPatternField(spec, where, problems);
71
+ exactKeys(spec, SAFE_NET_KEYS, where, problems);
72
+ for (const k of ['flags', 'values', 'local_values']) strList(spec[k], `${where}.${k}`, problems, /^--?[A-Za-z0-9][A-Za-z0-9-]{0,40}$/);
73
+ for (const k of ['short_flags', 'short_values']) if (typeof spec[k] !== 'string' || !/^[A-Za-z]{0,30}$/.test(spec[k])) problems.push(`${where}.${k} must be letters`);
74
+ if (typeof spec.items !== 'boolean') problems.push(`${where}.items must be true or false`);
75
+ }
76
+ }
77
+ if (!isObj(g.safe_net_aliases)) problems.push('guard.safe_net_aliases must be an object');
78
+ else for (const [alias, target] of Object.entries(g.safe_net_aliases)) {
79
+ if (!/^[a-z]{1,20}$/.test(alias) || typeof target !== 'string' || !(isObj(g.safe_net) && g.safe_net[target])) problems.push(`guard.safe_net_aliases.${alias} must name a program in safe_net`);
80
+ }
81
+ if (!Array.isArray(g.command_blocks)) problems.push('guard.command_blocks must be a list');
82
+ else {
83
+ if (g.command_blocks.length > 50) problems.push('guard.command_blocks has more than 50 items');
84
+ g.command_blocks.forEach((b, i) => {
85
+ const where = `guard.command_blocks[${i}]`;
86
+ if (!isObj(b)) { problems.push(`${where} must be an object`); return; }
87
+ noPatternField(b, where, problems);
88
+ exactKeys(b, BLOCK_KEYS, where, problems, ['programs']);
89
+ if (typeof b.id !== 'string' || !/^[a-z0-9-]{1,40}$/.test(b.id)) problems.push(`${where}.id must be short lowercase words`);
90
+ if (!['exfil', 'destroy', 'disarm'].includes(b.category)) problems.push(`${where}.category must be exfil, destroy or disarm`);
91
+ strList(b.all, `${where}.all`, problems);
92
+ if (Array.isArray(b.all) && !b.all.length) problems.push(`${where}.all needs at least one substring`);
93
+ if ('programs' in b) strList(b.programs, `${where}.programs`, problems, /^[A-Za-z0-9._+-]{1,40}$/);
94
+ });
95
+ }
96
+ }
97
+ const a = rules.agents;
98
+ if (!isObj(a)) problems.push('agents must be an object');
99
+ else {
100
+ noPatternField(a, 'agents', problems);
101
+ exactKeys(a, AGENT_KEYS, 'agents', problems);
102
+ strList(a.override_substrings, 'agents.override_substrings', problems);
103
+ strList(a.secret_env_names, 'agents.secret_env_names', problems, /^[A-Za-z_][A-Za-z0-9_.-]{0,80}$/);
104
+ }
105
+ return problems;
106
+ }
107
+
108
+ module.exports = { validateRules };
@@ -0,0 +1,237 @@
1
+ {
2
+ "version": 2,
3
+ "guard": {
4
+ "allow_hosts": [
5
+ "localhost",
6
+ "127.0.0.1",
7
+ "0.0.0.0",
8
+ "::1",
9
+ "api.github.com",
10
+ "github.com",
11
+ "uploads.github.com",
12
+ "api.openai.com",
13
+ "api.anthropic.com",
14
+ "registry.npmjs.org",
15
+ "pypi.org",
16
+ "upload.pypi.org",
17
+ "api.atris.ai",
18
+ "atris.ai",
19
+ "api.stripe.com",
20
+ "slack.com",
21
+ "api.vercel.com",
22
+ "api.render.com"
23
+ ],
24
+ "net_tools": [
25
+ "curl",
26
+ "wget",
27
+ "nc",
28
+ "ncat",
29
+ "netcat",
30
+ "socat",
31
+ "telnet",
32
+ "scp",
33
+ "sftp",
34
+ "ftp",
35
+ "rsync",
36
+ "http",
37
+ "https",
38
+ "xh",
39
+ "httpie",
40
+ "aria2c",
41
+ "lftp",
42
+ "ssh"
43
+ ],
44
+ "secret_parts": [
45
+ "KEY",
46
+ "KEYS",
47
+ "TOKEN",
48
+ "TOKENS",
49
+ "SECRET",
50
+ "SECRETS",
51
+ "PASSWORD",
52
+ "PASSWD",
53
+ "PASS",
54
+ "PWD",
55
+ "CREDENTIAL",
56
+ "CREDENTIALS",
57
+ "CREDS",
58
+ "AUTH",
59
+ "PRIVATE",
60
+ "SESSION",
61
+ "COOKIE",
62
+ "APIKEY",
63
+ "PAT",
64
+ "DSN"
65
+ ],
66
+ "safe_net": {
67
+ "curl": {
68
+ "flags": [
69
+ "-s",
70
+ "--silent",
71
+ "-S",
72
+ "--show-error",
73
+ "-f",
74
+ "--fail",
75
+ "--fail-with-body",
76
+ "--fail-early",
77
+ "-L",
78
+ "--location",
79
+ "-O",
80
+ "--remote-name",
81
+ "-i",
82
+ "--include",
83
+ "-v",
84
+ "--verbose",
85
+ "--compressed",
86
+ "-G",
87
+ "--get"
88
+ ],
89
+ "values": [
90
+ "-H",
91
+ "--header",
92
+ "-d",
93
+ "--data",
94
+ "--data-raw",
95
+ "--data-binary",
96
+ "--data-urlencode",
97
+ "--data-ascii",
98
+ "--json",
99
+ "-X",
100
+ "--request",
101
+ "-o",
102
+ "--output",
103
+ "-w",
104
+ "--write-out",
105
+ "-m",
106
+ "--max-time"
107
+ ],
108
+ "local_values": [
109
+ "-o",
110
+ "--output"
111
+ ],
112
+ "short_flags": "sSfLOivG",
113
+ "short_values": "HdXowm",
114
+ "items": false
115
+ },
116
+ "wget": {
117
+ "flags": [
118
+ "-q",
119
+ "--quiet",
120
+ "-S",
121
+ "--server-response",
122
+ "-nv",
123
+ "--no-verbose"
124
+ ],
125
+ "values": [
126
+ "-O",
127
+ "--output-document",
128
+ "--header",
129
+ "--post-data",
130
+ "--method",
131
+ "--body-data",
132
+ "-T",
133
+ "--timeout",
134
+ "-t",
135
+ "--tries"
136
+ ],
137
+ "local_values": [
138
+ "-O",
139
+ "--output-document"
140
+ ],
141
+ "short_flags": "qS",
142
+ "short_values": "OTt",
143
+ "items": false
144
+ },
145
+ "http": {
146
+ "flags": [
147
+ "-j",
148
+ "--json",
149
+ "-f",
150
+ "--form",
151
+ "-h",
152
+ "--headers",
153
+ "-b",
154
+ "--body",
155
+ "-v",
156
+ "--verbose",
157
+ "--check-status",
158
+ "--ignore-stdin"
159
+ ],
160
+ "values": [
161
+ "-p",
162
+ "--print",
163
+ "--timeout",
164
+ "-o",
165
+ "--output"
166
+ ],
167
+ "local_values": [
168
+ "-o",
169
+ "--output"
170
+ ],
171
+ "short_flags": "jfhbv",
172
+ "short_values": "po",
173
+ "items": true
174
+ }
175
+ },
176
+ "safe_net_aliases": {
177
+ "https": "http",
178
+ "xh": "http",
179
+ "httpie": "http"
180
+ },
181
+ "read_only": [
182
+ "cat",
183
+ "less",
184
+ "more",
185
+ "head",
186
+ "tail",
187
+ "grep",
188
+ "egrep",
189
+ "fgrep",
190
+ "rg",
191
+ "jq",
192
+ "ls",
193
+ "stat",
194
+ "wc",
195
+ "file",
196
+ "diff",
197
+ "echo",
198
+ "test",
199
+ "[",
200
+ "realpath",
201
+ "readlink"
202
+ ],
203
+ "read_only_git": [
204
+ "diff",
205
+ "log",
206
+ "show",
207
+ "status"
208
+ ],
209
+ "db_clients": [
210
+ "psql",
211
+ "pg_restore",
212
+ "mysql",
213
+ "mariadb",
214
+ "sqlite3",
215
+ "sqlite",
216
+ "sqlcmd",
217
+ "mongosh",
218
+ "mongo",
219
+ "redis-cli",
220
+ "clickhouse-client",
221
+ "clickhouse",
222
+ "cockroach",
223
+ "duckdb",
224
+ "supabase",
225
+ "prisma",
226
+ "turso",
227
+ "wrangler",
228
+ "neonctl",
229
+ "cqlsh"
230
+ ],
231
+ "command_blocks": []
232
+ },
233
+ "agents": {
234
+ "override_substrings": [],
235
+ "secret_env_names": []
236
+ }
237
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "atris",
3
- "version": "3.82.5",
3
+ "version": "3.82.7",
4
4
  "description": "you say what you want in plain words. atris builds it, checks it, and shows you proof. project memory your coding agents share across sessions.",
5
5
  "mcpName": "io.github.atrislabs/atris",
6
6
  "main": "bin/atris.js",