@safets-org/cli 1.0.1 → 1.0.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/README.md +34 -1
- package/dist/detectors/index.js +26 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -36,6 +36,16 @@ The npm package is scoped as `@safets-org/cli`, but the installed command is sti
|
|
|
36
36
|
|
|
37
37
|
## Usage
|
|
38
38
|
|
|
39
|
+
Run the installed `safets` binary with your package manager:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
npx safets doctor
|
|
43
|
+
pnpm exec safets doctor
|
|
44
|
+
bunx safets doctor
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Or add package scripts and run `safets` directly inside those scripts:
|
|
48
|
+
|
|
39
49
|
```bash
|
|
40
50
|
safets doctor
|
|
41
51
|
safets doctor --include-tests
|
|
@@ -106,6 +116,13 @@ No target fell back to AST-only mode. See [docs/real-world-validation.md](./docs
|
|
|
106
116
|
|
|
107
117
|
---
|
|
108
118
|
|
|
119
|
+
## End-To-End Guides
|
|
120
|
+
|
|
121
|
+
- [SafeTS documentation website](./docs-site/README.md)
|
|
122
|
+
- [TanStack Start, shadcn/ui, Tailwind CSS, and dark mode](./docs/tanstack-start-shadcn-tailwind.md)
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
109
126
|
## The 9 Patterns
|
|
110
127
|
|
|
111
128
|
| Pattern | Confidence | Example |
|
|
@@ -145,7 +162,7 @@ The baseline stores scan options such as `includeTests`. If you run `doctor --fa
|
|
|
145
162
|
SafeTS can run directly in GitHub Actions:
|
|
146
163
|
|
|
147
164
|
```yaml
|
|
148
|
-
- uses: Dioman-Keita/safets@v1.0.
|
|
165
|
+
- uses: Dioman-Keita/safets@v1.0.2
|
|
149
166
|
with:
|
|
150
167
|
fail-on-new: "true"
|
|
151
168
|
```
|
|
@@ -179,6 +196,22 @@ SafeTS releases follow the documented workflow in [docs/release.md](./docs/relea
|
|
|
179
196
|
|
|
180
197
|
Detector architecture and contribution expectations are documented in [docs/detectors.md](./docs/detectors.md).
|
|
181
198
|
|
|
199
|
+
## AI Agent Skill
|
|
200
|
+
|
|
201
|
+
SafeTS includes an installable skill for agents that support the `skills` CLI:
|
|
202
|
+
|
|
203
|
+
```bash
|
|
204
|
+
npx skills add Dioman-Keita/safets
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
For a non-interactive install, pass the target agent explicitly. For Codex:
|
|
208
|
+
|
|
209
|
+
```bash
|
|
210
|
+
npx skills add Dioman-Keita/safets --skill safets-agent -a codex -g -y
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
The skill is not coupled to Codex; Codex is only one supported target. After installation, restart the agent and invoke `$safets-agent` when an agent should run or interpret SafeTS on a TypeScript project.
|
|
214
|
+
|
|
182
215
|
## Roadmap
|
|
183
216
|
|
|
184
217
|
The launch plan is tracked in [ROADMAP.md](./ROADMAP.md).
|
package/dist/detectors/index.js
CHANGED
|
@@ -282,9 +282,35 @@ export function detectUnsafeEnvAccess(sf, checker) {
|
|
|
282
282
|
return false;
|
|
283
283
|
}
|
|
284
284
|
}
|
|
285
|
+
function isOptionalChainReceiver(node) {
|
|
286
|
+
let current = node;
|
|
287
|
+
while (ts.isParenthesizedExpression(current.parent)) {
|
|
288
|
+
current = current.parent;
|
|
289
|
+
}
|
|
290
|
+
const parent = current.parent;
|
|
291
|
+
const isOptionalReceiver = ((ts.isPropertyAccessExpression(parent) || ts.isElementAccessExpression(parent)) &&
|
|
292
|
+
parent.expression === current &&
|
|
293
|
+
parent.questionDotToken !== undefined) ||
|
|
294
|
+
(ts.isCallExpression(parent) &&
|
|
295
|
+
parent.expression === current &&
|
|
296
|
+
parent.questionDotToken !== undefined);
|
|
297
|
+
if (!isOptionalReceiver) {
|
|
298
|
+
return false;
|
|
299
|
+
}
|
|
300
|
+
let optionalAccess = parent;
|
|
301
|
+
let sawParentheses = false;
|
|
302
|
+
while (ts.isParenthesizedExpression(optionalAccess.parent)) {
|
|
303
|
+
sawParentheses = true;
|
|
304
|
+
optionalAccess = optionalAccess.parent;
|
|
305
|
+
}
|
|
306
|
+
return (!sawParentheses ||
|
|
307
|
+
!(ts.isCallExpression(optionalAccess.parent) &&
|
|
308
|
+
optionalAccess.parent.expression === optionalAccess));
|
|
309
|
+
}
|
|
285
310
|
function visit(node) {
|
|
286
311
|
if (isEnvAccess(node) &&
|
|
287
312
|
!isSafelyDefaulted(node) &&
|
|
313
|
+
!isOptionalChainReceiver(node) &&
|
|
288
314
|
!ts.isNonNullExpression(node.parent)) {
|
|
289
315
|
try {
|
|
290
316
|
const envVar = node.name.getText();
|