oats-scan 0.2.0__tar.gz
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.
- oats_scan-0.2.0/LICENSE +33 -0
- oats_scan-0.2.0/PKG-INFO +214 -0
- oats_scan-0.2.0/README.md +193 -0
- oats_scan-0.2.0/pyproject.toml +37 -0
- oats_scan-0.2.0/setup.cfg +4 -0
- oats_scan-0.2.0/src/oats_scan/__init__.py +9 -0
- oats_scan-0.2.0/src/oats_scan/__main__.py +5 -0
- oats_scan-0.2.0/src/oats_scan/cli.py +90 -0
- oats_scan-0.2.0/src/oats_scan/gateway.py +243 -0
- oats_scan-0.2.0/src/oats_scan/scan.py +53 -0
- oats_scan-0.2.0/src/oats_scan.egg-info/PKG-INFO +214 -0
- oats_scan-0.2.0/src/oats_scan.egg-info/SOURCES.txt +15 -0
- oats_scan-0.2.0/src/oats_scan.egg-info/dependency_links.txt +1 -0
- oats_scan-0.2.0/src/oats_scan.egg-info/entry_points.txt +2 -0
- oats_scan-0.2.0/src/oats_scan.egg-info/requires.txt +1 -0
- oats_scan-0.2.0/src/oats_scan.egg-info/top_level.txt +1 -0
- oats_scan-0.2.0/tests/test_scan.py +710 -0
oats_scan-0.2.0/LICENSE
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Pheo Inc.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
NOTE ON THE CLASSIFIER
|
|
26
|
+
|
|
27
|
+
The MIT licence above covers the Python source in this repository.
|
|
28
|
+
|
|
29
|
+
Since version 0.2.0, oats-scan bundles no binaries. The compiled classifier
|
|
30
|
+
it runs is part of pheo-oats, which oats-scan installs as a dependency;
|
|
31
|
+
pheo-oats is proprietary software of Pheo Inc., distributed under its own
|
|
32
|
+
terms. Versions before 0.2.0 bundled the same classifier under
|
|
33
|
+
src/oats_scan/_bin/, under the terms stated in those versions.
|
oats_scan-0.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: oats-scan
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: See what the agent skills on your machine tell an AI agent to run (the `oats scan` of pheo-oats)
|
|
5
|
+
Author-email: Pheo <hello@pheo.ai>
|
|
6
|
+
Project-URL: Homepage, https://pheo.ai
|
|
7
|
+
Project-URL: Source, https://github.com/pheo-ai/oats-scan
|
|
8
|
+
Project-URL: Issues, https://github.com/pheo-ai/oats-scan/issues
|
|
9
|
+
Keywords: ai,agents,agent-skills,security,governance,claude,cursor
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Environment :: Console
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Topic :: Security
|
|
15
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
16
|
+
Requires-Python: >=3.9
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
License-File: LICENSE
|
|
19
|
+
Requires-Dist: pheo-oats>=0.7.1
|
|
20
|
+
Dynamic: license-file
|
|
21
|
+
|
|
22
|
+
# oats-scan
|
|
23
|
+
|
|
24
|
+
**See what the agent skills on your machine tell an AI agent to run.**
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
pip install oats-scan
|
|
28
|
+
oats-scan
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Thirty seconds. No account, no sign up, no configuration, and nothing leaves
|
|
32
|
+
your machine.
|
|
33
|
+
|
|
34
|
+
**oats-scan is now the same scanner as `oats scan` in
|
|
35
|
+
[pheo-oats](https://pypi.org/project/pheo-oats/).** `pip install oats-scan`
|
|
36
|
+
installs pheo-oats, and `oats-scan` runs `oats scan` with the flags, defaults
|
|
37
|
+
and exit codes it always had, so the two print the same numbers. If you
|
|
38
|
+
already use pheo-oats, `oats scan` is all you need.
|
|
39
|
+
|
|
40
|
+
## You already installed these
|
|
41
|
+
|
|
42
|
+
Agent skills are markdown files that tell an AI what commands to run on your
|
|
43
|
+
computer. You install them the way people installed browser extensions in 2010:
|
|
44
|
+
on a recommendation, without reading them.
|
|
45
|
+
|
|
46
|
+
Your agent reads them. You usually don't.
|
|
47
|
+
|
|
48
|
+
`oats-scan` reads them for you. On a working laptop it found 1,528 commands
|
|
49
|
+
in 4,331 files, and 19 of them in classes that can never run unattended.
|
|
50
|
+
Nineteen. In plugins from well known vendors, all of them legitimate. Here is
|
|
51
|
+
what a run looks like, on a single skill file:
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
Scanning /home/you/skills
|
|
55
|
+
|
|
56
|
+
1 files, 5 shell blocks. Classifying ...
|
|
57
|
+
|
|
58
|
+
What these instruct an agent to do
|
|
59
|
+
|
|
60
|
+
Remote code execution 1 never graduates
|
|
61
|
+
Credential access 1 never graduates
|
|
62
|
+
Shell command 3
|
|
63
|
+
|
|
64
|
+
2 of 5 actions, in 2 classes, can never run unattended.
|
|
65
|
+
Those are the ones a person has to approve, every time.
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
One of the laptop's nineteen is this line, sitting in an installed skill,
|
|
69
|
+
waiting for the agent to decide to run it:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
curl -fsSL https://downloads.cursor.com/origin/install.sh | sh
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Nothing there is an attack. That is the point. **You still want to know.**
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
oats-scan --files # which skill each one came from
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Install
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
pip install oats-scan
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Python 3.9 or newer. oats-scan itself is pure Python; it installs pheo-oats,
|
|
88
|
+
which carries the compiled classifier.
|
|
89
|
+
|
|
90
|
+
**Platform support** is pheo-oats': wheels for macOS (Apple silicon and
|
|
91
|
+
Intel), Linux x86-64, Linux aarch64 and Windows x86-64, and `pip` downloads
|
|
92
|
+
only yours.
|
|
93
|
+
|
|
94
|
+
**Working from source.** This repository holds the Python and the tests. To run
|
|
95
|
+
a source checkout, install pheo-oats for the classifier and put the checkout
|
|
96
|
+
ahead of it:
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
git clone https://github.com/pheo-ai/oats-scan
|
|
100
|
+
cd oats-scan
|
|
101
|
+
pip install pheo-oats
|
|
102
|
+
PYTHONPATH=src python3 -m oats_scan --files
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Or point `OATS_SCAN_BIN_DIR` at a directory holding `pheo-action-gateway`,
|
|
106
|
+
`oatsctl` and `pheo-mcp-github`.
|
|
107
|
+
|
|
108
|
+
## What it looks at
|
|
109
|
+
|
|
110
|
+
With no arguments it reads the agent skills installed on this machine:
|
|
111
|
+
`~/.claude`, `~/.cursor`, `~/.codex`, `~/.openclaw`, `~/.aider`, `~/.windsurf`,
|
|
112
|
+
`~/.gemini`, `~/.continue`, plus the directory you are standing in.
|
|
113
|
+
|
|
114
|
+
Give it a path to read somewhere specific:
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
oats-scan ~/my-project
|
|
118
|
+
oats-scan --json # machine readable
|
|
119
|
+
oats-scan --strict # exit 1 if anything needs review, for CI
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## How it decides
|
|
123
|
+
|
|
124
|
+
Every command is sorted into a **consequence class**, each carrying a severity
|
|
125
|
+
from 0 to 100. Classes at or above 75 are the ones that can never become
|
|
126
|
+
routine, however well an agent has behaved. Those are what `never graduates`
|
|
127
|
+
marks.
|
|
128
|
+
|
|
129
|
+
Some things you can take back:
|
|
130
|
+
|
|
131
|
+
- Agent writes a bad document, you fix the document
|
|
132
|
+
- Agent opens a pull request, you close it
|
|
133
|
+
|
|
134
|
+
Some you can't:
|
|
135
|
+
|
|
136
|
+
- Agent deletes a folder, the files are gone
|
|
137
|
+
- Agent pushes to production, your customers already saw it
|
|
138
|
+
- Agent reads your password file, it has your password now
|
|
139
|
+
- Agent runs a script off the internet, whatever it did, it did
|
|
140
|
+
|
|
141
|
+
The classifier is deterministic. No model, no inference, no network call. It
|
|
142
|
+
reads the command string and nothing else, so the same command produces the
|
|
143
|
+
same class today, next year, and on your machine. Any result can be re-derived
|
|
144
|
+
without re-running anything.
|
|
145
|
+
|
|
146
|
+
The full list of classes ships with pheo-oats as
|
|
147
|
+
`pheo_oats/data/action-classes.json`, the same list the Pheo space and the
|
|
148
|
+
OATS gateway rule with.
|
|
149
|
+
|
|
150
|
+
## What it tells you, and what it leaves to you
|
|
151
|
+
|
|
152
|
+
`oats-scan` reports the **class** of each action. It does not decide whether
|
|
153
|
+
that action is acceptable, because that answer is yours.
|
|
154
|
+
|
|
155
|
+
A startup will let an agent install packages all day. A bank will not let one
|
|
156
|
+
read a credential file, ever. Both are right. They are different companies with
|
|
157
|
+
different blast radii. So this gives you the view and leaves the limit to you.
|
|
158
|
+
|
|
159
|
+
Everything it reports is normal software doing normal things. Installers
|
|
160
|
+
download and run code, because that is what installers are for. Some of those
|
|
161
|
+
actions happen to be ones you cannot undo, and those are the ones worth a look.
|
|
162
|
+
|
|
163
|
+
## How it works with your other tools
|
|
164
|
+
|
|
165
|
+
`oats-scan` reads actions. Registry scanners read artifacts. The two see
|
|
166
|
+
different things and both are worth having.
|
|
167
|
+
|
|
168
|
+
Scanners are the right tool for harm that never becomes an action: a hardcoded
|
|
169
|
+
recipient, an undisclosed scope, an instruction written to talk an agent into
|
|
170
|
+
misbehaving. `oats-scan` is the right tool for what the agent then goes and
|
|
171
|
+
does. Run both and you cover both.
|
|
172
|
+
|
|
173
|
+
## Honest limits
|
|
174
|
+
|
|
175
|
+
**These counts are a floor.** Only fenced shell content is read. Prose is
|
|
176
|
+
ignored, because "this skill can delete your notes" is a sentence rather than
|
|
177
|
+
an action. A block is skipped unless its first command is a recognised one. The
|
|
178
|
+
real number is what you see here or higher.
|
|
179
|
+
|
|
180
|
+
**Precision is 92%**, with a 95% confidence interval of [84.8, 96.5] on a
|
|
181
|
+
hand adjudicated sample of 100. The known failure mode is content that looks
|
|
182
|
+
like a command inside a heredoc. When that happens the extra line is visible in
|
|
183
|
+
`--files`, so you can see it and judge for yourself.
|
|
184
|
+
|
|
185
|
+
**Unresolved blocks are counted separately.** A block the classifier could not
|
|
186
|
+
read is reported on its own line rather than folded in with the clean ones.
|
|
187
|
+
|
|
188
|
+
## Privacy
|
|
189
|
+
|
|
190
|
+
Everything runs on your machine. The classifier is a local process listening on
|
|
191
|
+
127.0.0.1, started for the length of the scan and shut down afterwards. It
|
|
192
|
+
writes to a temporary database that is deleted when the scan ends, so a scan
|
|
193
|
+
never touches state you rely on. No telemetry, no account, no network calls.
|
|
194
|
+
|
|
195
|
+
## Where this comes from
|
|
196
|
+
|
|
197
|
+
The classifier is the resolver from the
|
|
198
|
+
[Open Agent Trust System](https://github.com/pheo-ai/open-agent-trust-system),
|
|
199
|
+
the same one that runs in front of live agents deciding whether an action
|
|
200
|
+
executes. The measurement study behind the class list covers 66,192 public
|
|
201
|
+
agent skills, and the classification of all of them is published as
|
|
202
|
+
[pheo-ai/clawhub-consequence-classes](https://huggingface.co/datasets/pheo-ai/clawhub-consequence-classes).
|
|
203
|
+
|
|
204
|
+
Once you can see what your agent is told to do, the next question is usually
|
|
205
|
+
whether something can hold an action while you look at it. That is
|
|
206
|
+
[Pheo OATS](https://pheo.ai), the gateway this classifier normally lives in,
|
|
207
|
+
and it is already installed: `oats quickstart claude` starts it. `oats-scan` is
|
|
208
|
+
the view. The gateway is the brake. Start with the view.
|
|
209
|
+
|
|
210
|
+
## Licence
|
|
211
|
+
|
|
212
|
+
The Python source in this repository is MIT. The compiled classifier is part of
|
|
213
|
+
pheo-oats, proprietary software of Pheo Inc. installed as a dependency under
|
|
214
|
+
its own terms. See [LICENSE](LICENSE).
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
# oats-scan
|
|
2
|
+
|
|
3
|
+
**See what the agent skills on your machine tell an AI agent to run.**
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
pip install oats-scan
|
|
7
|
+
oats-scan
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
Thirty seconds. No account, no sign up, no configuration, and nothing leaves
|
|
11
|
+
your machine.
|
|
12
|
+
|
|
13
|
+
**oats-scan is now the same scanner as `oats scan` in
|
|
14
|
+
[pheo-oats](https://pypi.org/project/pheo-oats/).** `pip install oats-scan`
|
|
15
|
+
installs pheo-oats, and `oats-scan` runs `oats scan` with the flags, defaults
|
|
16
|
+
and exit codes it always had, so the two print the same numbers. If you
|
|
17
|
+
already use pheo-oats, `oats scan` is all you need.
|
|
18
|
+
|
|
19
|
+
## You already installed these
|
|
20
|
+
|
|
21
|
+
Agent skills are markdown files that tell an AI what commands to run on your
|
|
22
|
+
computer. You install them the way people installed browser extensions in 2010:
|
|
23
|
+
on a recommendation, without reading them.
|
|
24
|
+
|
|
25
|
+
Your agent reads them. You usually don't.
|
|
26
|
+
|
|
27
|
+
`oats-scan` reads them for you. On a working laptop it found 1,528 commands
|
|
28
|
+
in 4,331 files, and 19 of them in classes that can never run unattended.
|
|
29
|
+
Nineteen. In plugins from well known vendors, all of them legitimate. Here is
|
|
30
|
+
what a run looks like, on a single skill file:
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
Scanning /home/you/skills
|
|
34
|
+
|
|
35
|
+
1 files, 5 shell blocks. Classifying ...
|
|
36
|
+
|
|
37
|
+
What these instruct an agent to do
|
|
38
|
+
|
|
39
|
+
Remote code execution 1 never graduates
|
|
40
|
+
Credential access 1 never graduates
|
|
41
|
+
Shell command 3
|
|
42
|
+
|
|
43
|
+
2 of 5 actions, in 2 classes, can never run unattended.
|
|
44
|
+
Those are the ones a person has to approve, every time.
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
One of the laptop's nineteen is this line, sitting in an installed skill,
|
|
48
|
+
waiting for the agent to decide to run it:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
curl -fsSL https://downloads.cursor.com/origin/install.sh | sh
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Nothing there is an attack. That is the point. **You still want to know.**
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
oats-scan --files # which skill each one came from
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Install
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
pip install oats-scan
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Python 3.9 or newer. oats-scan itself is pure Python; it installs pheo-oats,
|
|
67
|
+
which carries the compiled classifier.
|
|
68
|
+
|
|
69
|
+
**Platform support** is pheo-oats': wheels for macOS (Apple silicon and
|
|
70
|
+
Intel), Linux x86-64, Linux aarch64 and Windows x86-64, and `pip` downloads
|
|
71
|
+
only yours.
|
|
72
|
+
|
|
73
|
+
**Working from source.** This repository holds the Python and the tests. To run
|
|
74
|
+
a source checkout, install pheo-oats for the classifier and put the checkout
|
|
75
|
+
ahead of it:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
git clone https://github.com/pheo-ai/oats-scan
|
|
79
|
+
cd oats-scan
|
|
80
|
+
pip install pheo-oats
|
|
81
|
+
PYTHONPATH=src python3 -m oats_scan --files
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Or point `OATS_SCAN_BIN_DIR` at a directory holding `pheo-action-gateway`,
|
|
85
|
+
`oatsctl` and `pheo-mcp-github`.
|
|
86
|
+
|
|
87
|
+
## What it looks at
|
|
88
|
+
|
|
89
|
+
With no arguments it reads the agent skills installed on this machine:
|
|
90
|
+
`~/.claude`, `~/.cursor`, `~/.codex`, `~/.openclaw`, `~/.aider`, `~/.windsurf`,
|
|
91
|
+
`~/.gemini`, `~/.continue`, plus the directory you are standing in.
|
|
92
|
+
|
|
93
|
+
Give it a path to read somewhere specific:
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
oats-scan ~/my-project
|
|
97
|
+
oats-scan --json # machine readable
|
|
98
|
+
oats-scan --strict # exit 1 if anything needs review, for CI
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## How it decides
|
|
102
|
+
|
|
103
|
+
Every command is sorted into a **consequence class**, each carrying a severity
|
|
104
|
+
from 0 to 100. Classes at or above 75 are the ones that can never become
|
|
105
|
+
routine, however well an agent has behaved. Those are what `never graduates`
|
|
106
|
+
marks.
|
|
107
|
+
|
|
108
|
+
Some things you can take back:
|
|
109
|
+
|
|
110
|
+
- Agent writes a bad document, you fix the document
|
|
111
|
+
- Agent opens a pull request, you close it
|
|
112
|
+
|
|
113
|
+
Some you can't:
|
|
114
|
+
|
|
115
|
+
- Agent deletes a folder, the files are gone
|
|
116
|
+
- Agent pushes to production, your customers already saw it
|
|
117
|
+
- Agent reads your password file, it has your password now
|
|
118
|
+
- Agent runs a script off the internet, whatever it did, it did
|
|
119
|
+
|
|
120
|
+
The classifier is deterministic. No model, no inference, no network call. It
|
|
121
|
+
reads the command string and nothing else, so the same command produces the
|
|
122
|
+
same class today, next year, and on your machine. Any result can be re-derived
|
|
123
|
+
without re-running anything.
|
|
124
|
+
|
|
125
|
+
The full list of classes ships with pheo-oats as
|
|
126
|
+
`pheo_oats/data/action-classes.json`, the same list the Pheo space and the
|
|
127
|
+
OATS gateway rule with.
|
|
128
|
+
|
|
129
|
+
## What it tells you, and what it leaves to you
|
|
130
|
+
|
|
131
|
+
`oats-scan` reports the **class** of each action. It does not decide whether
|
|
132
|
+
that action is acceptable, because that answer is yours.
|
|
133
|
+
|
|
134
|
+
A startup will let an agent install packages all day. A bank will not let one
|
|
135
|
+
read a credential file, ever. Both are right. They are different companies with
|
|
136
|
+
different blast radii. So this gives you the view and leaves the limit to you.
|
|
137
|
+
|
|
138
|
+
Everything it reports is normal software doing normal things. Installers
|
|
139
|
+
download and run code, because that is what installers are for. Some of those
|
|
140
|
+
actions happen to be ones you cannot undo, and those are the ones worth a look.
|
|
141
|
+
|
|
142
|
+
## How it works with your other tools
|
|
143
|
+
|
|
144
|
+
`oats-scan` reads actions. Registry scanners read artifacts. The two see
|
|
145
|
+
different things and both are worth having.
|
|
146
|
+
|
|
147
|
+
Scanners are the right tool for harm that never becomes an action: a hardcoded
|
|
148
|
+
recipient, an undisclosed scope, an instruction written to talk an agent into
|
|
149
|
+
misbehaving. `oats-scan` is the right tool for what the agent then goes and
|
|
150
|
+
does. Run both and you cover both.
|
|
151
|
+
|
|
152
|
+
## Honest limits
|
|
153
|
+
|
|
154
|
+
**These counts are a floor.** Only fenced shell content is read. Prose is
|
|
155
|
+
ignored, because "this skill can delete your notes" is a sentence rather than
|
|
156
|
+
an action. A block is skipped unless its first command is a recognised one. The
|
|
157
|
+
real number is what you see here or higher.
|
|
158
|
+
|
|
159
|
+
**Precision is 92%**, with a 95% confidence interval of [84.8, 96.5] on a
|
|
160
|
+
hand adjudicated sample of 100. The known failure mode is content that looks
|
|
161
|
+
like a command inside a heredoc. When that happens the extra line is visible in
|
|
162
|
+
`--files`, so you can see it and judge for yourself.
|
|
163
|
+
|
|
164
|
+
**Unresolved blocks are counted separately.** A block the classifier could not
|
|
165
|
+
read is reported on its own line rather than folded in with the clean ones.
|
|
166
|
+
|
|
167
|
+
## Privacy
|
|
168
|
+
|
|
169
|
+
Everything runs on your machine. The classifier is a local process listening on
|
|
170
|
+
127.0.0.1, started for the length of the scan and shut down afterwards. It
|
|
171
|
+
writes to a temporary database that is deleted when the scan ends, so a scan
|
|
172
|
+
never touches state you rely on. No telemetry, no account, no network calls.
|
|
173
|
+
|
|
174
|
+
## Where this comes from
|
|
175
|
+
|
|
176
|
+
The classifier is the resolver from the
|
|
177
|
+
[Open Agent Trust System](https://github.com/pheo-ai/open-agent-trust-system),
|
|
178
|
+
the same one that runs in front of live agents deciding whether an action
|
|
179
|
+
executes. The measurement study behind the class list covers 66,192 public
|
|
180
|
+
agent skills, and the classification of all of them is published as
|
|
181
|
+
[pheo-ai/clawhub-consequence-classes](https://huggingface.co/datasets/pheo-ai/clawhub-consequence-classes).
|
|
182
|
+
|
|
183
|
+
Once you can see what your agent is told to do, the next question is usually
|
|
184
|
+
whether something can hold an action while you look at it. That is
|
|
185
|
+
[Pheo OATS](https://pheo.ai), the gateway this classifier normally lives in,
|
|
186
|
+
and it is already installed: `oats quickstart claude` starts it. `oats-scan` is
|
|
187
|
+
the view. The gateway is the brake. Start with the view.
|
|
188
|
+
|
|
189
|
+
## Licence
|
|
190
|
+
|
|
191
|
+
The Python source in this repository is MIT. The compiled classifier is part of
|
|
192
|
+
pheo-oats, proprietary software of Pheo Inc. installed as a dependency under
|
|
193
|
+
its own terms. See [LICENSE](LICENSE).
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "oats-scan"
|
|
7
|
+
version = "0.2.0"
|
|
8
|
+
description = "See what the agent skills on your machine tell an AI agent to run (the `oats scan` of pheo-oats)"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
authors = [{name = "Pheo", email = "hello@pheo.ai"}]
|
|
12
|
+
keywords = ["ai", "agents", "agent-skills", "security", "governance", "claude", "cursor"]
|
|
13
|
+
classifiers = [
|
|
14
|
+
"Development Status :: 4 - Beta",
|
|
15
|
+
"Environment :: Console",
|
|
16
|
+
"Intended Audience :: Developers",
|
|
17
|
+
"Programming Language :: Python :: 3",
|
|
18
|
+
"Topic :: Security",
|
|
19
|
+
"Topic :: Software Development :: Quality Assurance",
|
|
20
|
+
]
|
|
21
|
+
# The scanner and its classifier are pheo-oats'. 0.7.1 is the first that
|
|
22
|
+
# does everything oats-scan did (see pheo-oats' CHANGELOG).
|
|
23
|
+
dependencies = ["pheo-oats>=0.7.1"]
|
|
24
|
+
|
|
25
|
+
[project.urls]
|
|
26
|
+
Homepage = "https://pheo.ai"
|
|
27
|
+
Source = "https://github.com/pheo-ai/oats-scan"
|
|
28
|
+
Issues = "https://github.com/pheo-ai/oats-scan/issues"
|
|
29
|
+
|
|
30
|
+
[project.scripts]
|
|
31
|
+
oats-scan = "oats_scan.cli:main"
|
|
32
|
+
|
|
33
|
+
[tool.setuptools]
|
|
34
|
+
package-dir = {"" = "src"}
|
|
35
|
+
|
|
36
|
+
[tool.setuptools.packages.find]
|
|
37
|
+
where = ["src"]
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"""Command line entry point for oats-scan.
|
|
2
|
+
|
|
3
|
+
oats-scan is `oats scan` from pheo-oats under its old name: the same
|
|
4
|
+
scanner, so the two print the same numbers, with the flags, defaults and
|
|
5
|
+
exit codes oats-scan has always had (0 clean, 1 when --strict finds
|
|
6
|
+
something that needs review, 2 when the scan could not run, 130 on
|
|
7
|
+
Ctrl-C).
|
|
8
|
+
"""
|
|
9
|
+
import argparse
|
|
10
|
+
import os
|
|
11
|
+
import sys
|
|
12
|
+
|
|
13
|
+
from oats_scan import __version__
|
|
14
|
+
from oats_scan.gateway import ScanError
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def build_parser():
|
|
18
|
+
parser = argparse.ArgumentParser(
|
|
19
|
+
prog="oats-scan",
|
|
20
|
+
description=(
|
|
21
|
+
"Show what the agent skills on this machine instruct an AI agent "
|
|
22
|
+
"to run, sorted by what kind of effect each command has. The same "
|
|
23
|
+
"scanner as `oats scan` in pheo-oats."
|
|
24
|
+
),
|
|
25
|
+
epilog=(
|
|
26
|
+
"examples:\n"
|
|
27
|
+
"\n"
|
|
28
|
+
" oats-scan # skills installed on this machine\n"
|
|
29
|
+
" oats-scan --files # and which file each one came from\n"
|
|
30
|
+
" oats-scan ~/my-project # a specific directory\n"
|
|
31
|
+
" oats-scan --json # machine readable\n"
|
|
32
|
+
"\n"
|
|
33
|
+
"Nothing is installed, nothing is changed, and nothing leaves\n"
|
|
34
|
+
"this machine.\n"
|
|
35
|
+
),
|
|
36
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
37
|
+
)
|
|
38
|
+
parser.add_argument(
|
|
39
|
+
"path", nargs="?", default=None,
|
|
40
|
+
help="Directory to scan. Default: agent skills installed on this "
|
|
41
|
+
"machine, plus the current directory.",
|
|
42
|
+
)
|
|
43
|
+
parser.add_argument("--files", action="store_true",
|
|
44
|
+
help="List which files carry the actions worth reviewing")
|
|
45
|
+
parser.add_argument("--json", action="store_true", help="Machine readable output")
|
|
46
|
+
parser.add_argument("--strict", action="store_true",
|
|
47
|
+
help="Exit 1 if anything needs review, for CI")
|
|
48
|
+
parser.add_argument("--workers", type=int, default=8,
|
|
49
|
+
help="Parallel classifications (default 8)")
|
|
50
|
+
parser.add_argument("--version", action="version", version=__version__)
|
|
51
|
+
return parser
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def main(argv=None):
|
|
55
|
+
try:
|
|
56
|
+
sys.stdout.reconfigure(line_buffering=True)
|
|
57
|
+
except (AttributeError, ValueError):
|
|
58
|
+
pass
|
|
59
|
+
args = build_parser().parse_args(argv)
|
|
60
|
+
# OATS_SCAN_BIN_DIR keeps pointing the scan at its binaries, now that
|
|
61
|
+
# they are pheo-oats' and pheo-oats looks for PHEO_OATS_BIN_DIR.
|
|
62
|
+
if os.environ.get("OATS_SCAN_BIN_DIR") and not os.environ.get("PHEO_OATS_BIN_DIR"):
|
|
63
|
+
os.environ["PHEO_OATS_BIN_DIR"] = os.environ["OATS_SCAN_BIN_DIR"]
|
|
64
|
+
try:
|
|
65
|
+
from pheo_oats.cli import OatsError
|
|
66
|
+
except ImportError: # no pheo-oats: importing the scan says so
|
|
67
|
+
OatsError = ScanError
|
|
68
|
+
|
|
69
|
+
try:
|
|
70
|
+
from oats_scan.scan import run
|
|
71
|
+
|
|
72
|
+
return run(args)
|
|
73
|
+
except (ScanError, OatsError) as error:
|
|
74
|
+
print("\n{}".format(error), file=sys.stderr)
|
|
75
|
+
return 2
|
|
76
|
+
except SystemExit as error:
|
|
77
|
+
# pheo-oats reports a missing binary, or a classifier that would not
|
|
78
|
+
# start, as SystemExit with a sentence. Here that is exit status 2,
|
|
79
|
+
# which --strict never uses, so CI can tell "could not scan" from
|
|
80
|
+
# "found something".
|
|
81
|
+
if isinstance(error.code, str):
|
|
82
|
+
print("\n{}".format(error.code), file=sys.stderr)
|
|
83
|
+
return 2
|
|
84
|
+
raise
|
|
85
|
+
except KeyboardInterrupt:
|
|
86
|
+
return 130
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
if __name__ == "__main__":
|
|
90
|
+
sys.exit(main())
|