@safets-org/cli 1.0.0 → 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 +46 -1
- package/dist/detectors/index.js +26 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -16,6 +16,18 @@ Cannot read properties of undefined (reading 'name')
|
|
|
16
16
|
npm install --save-dev @safets-org/cli typescript
|
|
17
17
|
```
|
|
18
18
|
|
|
19
|
+
With Bun:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
bun add -D @safets-org/cli typescript
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
With pnpm:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
pnpm add -D @safets-org/cli typescript
|
|
29
|
+
```
|
|
30
|
+
|
|
19
31
|
No runtime TypeScript loader is required. SafeTS uses the TypeScript compiler already in your project.
|
|
20
32
|
|
|
21
33
|
The npm package is scoped as `@safets-org/cli`, but the installed command is still `safets`.
|
|
@@ -24,6 +36,16 @@ The npm package is scoped as `@safets-org/cli`, but the installed command is sti
|
|
|
24
36
|
|
|
25
37
|
## Usage
|
|
26
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
|
+
|
|
27
49
|
```bash
|
|
28
50
|
safets doctor
|
|
29
51
|
safets doctor --include-tests
|
|
@@ -94,6 +116,13 @@ No target fell back to AST-only mode. See [docs/real-world-validation.md](./docs
|
|
|
94
116
|
|
|
95
117
|
---
|
|
96
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
|
+
|
|
97
126
|
## The 9 Patterns
|
|
98
127
|
|
|
99
128
|
| Pattern | Confidence | Example |
|
|
@@ -133,7 +162,7 @@ The baseline stores scan options such as `includeTests`. If you run `doctor --fa
|
|
|
133
162
|
SafeTS can run directly in GitHub Actions:
|
|
134
163
|
|
|
135
164
|
```yaml
|
|
136
|
-
- uses: Dioman-Keita/safets@v1.0.
|
|
165
|
+
- uses: Dioman-Keita/safets@v1.0.2
|
|
137
166
|
with:
|
|
138
167
|
fail-on-new: "true"
|
|
139
168
|
```
|
|
@@ -167,6 +196,22 @@ SafeTS releases follow the documented workflow in [docs/release.md](./docs/relea
|
|
|
167
196
|
|
|
168
197
|
Detector architecture and contribution expectations are documented in [docs/detectors.md](./docs/detectors.md).
|
|
169
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
|
+
|
|
170
215
|
## Roadmap
|
|
171
216
|
|
|
172
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();
|