@pensar/apex 0.0.109 → 0.0.110-canary.a34d3e3d
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 +57 -23
- package/build/auth.js +1 -1
- package/build/index.js +71 -36
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
<h1 align="center">Pensar Apex</h1>
|
|
2
2
|
|
|
3
|
-
<p align="center">AI-powered penetration testing
|
|
3
|
+
<p align="center">AI-powered penetration testing using an AI agent to perform comprehensive blackbox and whitebox pentesting - directly in your terminal.
|
|
4
|
+
</p>
|
|
5
|
+
|
|
6
|
+
<p align="center">
|
|
7
|
+
Want to run from the cloud or integrate it with your CI/CD? See <a href="https://docs.pensar.dev/console">Pensar Console</a>.
|
|
4
8
|
</p>
|
|
5
9
|
|
|
6
10
|
<p align="center">
|
|
@@ -12,9 +16,57 @@
|
|
|
12
16
|
<a href="https://discord.gg/pensar"><img src="https://img.shields.io/badge/Discord-Join%20Us-5865F2?logo=discord&logoColor=white" alt="Discord"></a>
|
|
13
17
|
</p>
|
|
14
18
|
|
|
15
|
-
<p align="center">
|
|
19
|
+
<!-- <p align="center">
|
|
16
20
|
<img src="screenshot.png" alt="Pensar Apex Screenshot" width="800">
|
|
17
|
-
</p>
|
|
21
|
+
</p> -->
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
## Use Cases
|
|
25
|
+
|
|
26
|
+
Apex enables both developers and security professionals to run autonomous and assisted penetration testing directly from the terminal.
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
### Developers: Run a Pentest in Minutes
|
|
30
|
+
|
|
31
|
+
Apex makes it easy for developers to run a real penetration test without needing deep offensive security expertise.
|
|
32
|
+
|
|
33
|
+
Using the autonomous `/pentest` mode, Apex will perform reconnaissance, attack surface discovery, vulnerability testing, and exploitation attempts automatically.
|
|
34
|
+
|
|
35
|
+
This allows teams to quickly identify security issues before they reach production.
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
/pentest
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Examples:
|
|
42
|
+
- Test a staging environment before deploying
|
|
43
|
+
- Scan a newly launched domain or API
|
|
44
|
+
- Run quick security checks during development
|
|
45
|
+
- Identify exposed services or misconfigurations
|
|
46
|
+
|
|
47
|
+
This is the **fastest way to get real pentesting coverage without becoming a security expert.**
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
### Security Engineers: Advanced Operator Workflows
|
|
52
|
+
|
|
53
|
+
Security professionals can use Apex as an **agentic offensive security harness** that orchestrates tools and reasoning workflows.
|
|
54
|
+
|
|
55
|
+
The `/operator` mode allows engineers to work interactively with the Offensive Security Agent, guiding investigations and chaining tools dynamically.
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
/operator
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Examples:
|
|
63
|
+
- Deep investigation of suspicious endpoints
|
|
64
|
+
- Manual exploitation of discovered vulnerabilities
|
|
65
|
+
- Tool orchestration across recon and exploitation phases
|
|
66
|
+
- Validation and reproduction of vulnerabilities
|
|
67
|
+
- Open-source security research / testing
|
|
68
|
+
|
|
69
|
+
This turns Apex into a **terminal-native AI pentesting partner** rather than just a scanner.
|
|
18
70
|
|
|
19
71
|
## Installation
|
|
20
72
|
|
|
@@ -45,17 +97,13 @@ npm install -g @pensar/apex
|
|
|
45
97
|
|
|
46
98
|
## Usage
|
|
47
99
|
|
|
48
|
-
|
|
100
|
+
Open the Apex TUI:
|
|
49
101
|
|
|
50
102
|
```bash
|
|
51
103
|
pensar
|
|
52
104
|
```
|
|
53
105
|
|
|
54
|
-
##
|
|
55
|
-
|
|
56
|
-
Apex supports **OpenAI**, **Anthropic**, **AWS Bedrock**, and **vLLM** (local models). **Anthropic models provide the best performance** and are recommended for optimal results.
|
|
57
|
-
|
|
58
|
-
## Kali Linux Container (Recommended)
|
|
106
|
+
## Kali Linux Container (Optional)
|
|
59
107
|
|
|
60
108
|
For **best performance**, run Apex in the included Kali Linux container with preconfigured pentest tools:
|
|
61
109
|
|
|
@@ -72,20 +120,6 @@ Inside the container, run:
|
|
|
72
120
|
pensar
|
|
73
121
|
```
|
|
74
122
|
|
|
75
|
-
**Note:** On Linux hosts, consider using `network_mode: host` in `docker-compose.yml` for comprehensive network scanning.
|
|
76
|
-
|
|
77
|
-
## vLLM Local Model Support
|
|
78
|
-
|
|
79
|
-
To use a local vLLM server:
|
|
80
|
-
|
|
81
|
-
1. Set the vLLM endpoint:
|
|
82
|
-
|
|
83
|
-
```bash
|
|
84
|
-
export LOCAL_MODEL_URL="http://localhost:8000/v1"
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
2. In the Apex Models screen, enter your model name in the "Custom local model (vLLM)" input.
|
|
88
|
-
|
|
89
123
|
---
|
|
90
124
|
|
|
91
125
|
### ⚠️ Responsible Use
|
package/build/auth.js
CHANGED
|
@@ -8,7 +8,7 @@ import fs from "fs/promises";
|
|
|
8
8
|
// package.json
|
|
9
9
|
var package_default = {
|
|
10
10
|
name: "@pensar/apex",
|
|
11
|
-
version: "0.0.
|
|
11
|
+
version: "0.0.110-canary.a34d3e3d",
|
|
12
12
|
description: "AI-powered penetration testing CLI tool with terminal UI",
|
|
13
13
|
module: "src/tui/index.tsx",
|
|
14
14
|
main: "build/index.js",
|
package/build/index.js
CHANGED
|
@@ -31892,12 +31892,6 @@ var init_pensar = __esm(() => {
|
|
|
31892
31892
|
provider: "pensar",
|
|
31893
31893
|
contextLength: 200000
|
|
31894
31894
|
},
|
|
31895
|
-
{
|
|
31896
|
-
id: "pensar:anthropic.claude-opus-4-1-20250805-v1:0",
|
|
31897
|
-
name: "Claude Opus 4.1 (Pensar)",
|
|
31898
|
-
provider: "pensar",
|
|
31899
|
-
contextLength: 200000
|
|
31900
|
-
},
|
|
31901
31895
|
{
|
|
31902
31896
|
id: "pensar:anthropic.claude-haiku-4-5-20251001-v1:0",
|
|
31903
31897
|
name: "Claude Haiku 4.5 (Pensar)",
|
|
@@ -31977,7 +31971,7 @@ var package_default2;
|
|
|
31977
31971
|
var init_package = __esm(() => {
|
|
31978
31972
|
package_default2 = {
|
|
31979
31973
|
name: "@pensar/apex",
|
|
31980
|
-
version: "0.0.
|
|
31974
|
+
version: "0.0.110-canary.a34d3e3d",
|
|
31981
31975
|
description: "AI-powered penetration testing CLI tool with terminal UI",
|
|
31982
31976
|
module: "src/tui/index.tsx",
|
|
31983
31977
|
main: "build/index.js",
|
|
@@ -273281,7 +273275,7 @@ var PROVIDER_PREFERENCE_ORDER = [
|
|
|
273281
273275
|
"bedrock"
|
|
273282
273276
|
];
|
|
273283
273277
|
var PREFERRED_MODEL_BY_PROVIDER = {
|
|
273284
|
-
pensar: "pensar:anthropic.claude-opus-4-
|
|
273278
|
+
pensar: "pensar:anthropic.claude-opus-4-6-v1",
|
|
273285
273279
|
anthropic: "claude-opus-4-6",
|
|
273286
273280
|
openai: "gpt-5.2-pro",
|
|
273287
273281
|
google: "gemini-3.1-pro-preview",
|
|
@@ -273800,7 +273794,9 @@ function Footer({
|
|
|
273800
273794
|
const { colors: colors2 } = useTheme();
|
|
273801
273795
|
const { model, isExecuting, sessionCwd } = useAgent();
|
|
273802
273796
|
const effectiveCwd = sessionCwd || cwd;
|
|
273803
|
-
const
|
|
273797
|
+
const relativeCwd = effectiveCwd.split(os6.homedir()).pop() || "";
|
|
273798
|
+
const segments = relativeCwd.split("/").filter(Boolean);
|
|
273799
|
+
const displayCwd = segments.length <= 2 ? "~/" + segments.join("/") : "…/" + segments.slice(-2).join("/");
|
|
273804
273800
|
const session = useSession();
|
|
273805
273801
|
const route = useRoute();
|
|
273806
273802
|
const { isInputEmpty } = useInput();
|
|
@@ -273813,11 +273809,15 @@ function Footer({
|
|
|
273813
273809
|
justifyContent: "space-between",
|
|
273814
273810
|
width: "100%",
|
|
273815
273811
|
maxWidth: "100%",
|
|
273812
|
+
height: 1,
|
|
273816
273813
|
flexShrink: 0,
|
|
273814
|
+
overflow: "hidden",
|
|
273817
273815
|
children: [
|
|
273818
273816
|
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
|
|
273819
273817
|
flexDirection: "row",
|
|
273820
273818
|
gap: 1,
|
|
273819
|
+
flexShrink: 1,
|
|
273820
|
+
overflow: "hidden",
|
|
273821
273821
|
children: [
|
|
273822
273822
|
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
|
|
273823
273823
|
fg: colors2.textMuted,
|
|
@@ -273850,6 +273850,7 @@ function Footer({
|
|
|
273850
273850
|
showExitWarning ? /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
|
|
273851
273851
|
flexDirection: "row",
|
|
273852
273852
|
gap: 1,
|
|
273853
|
+
flexShrink: 0,
|
|
273853
273854
|
children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
|
|
273854
273855
|
fg: colors2.warning,
|
|
273855
273856
|
children: "⚠ Press Ctrl+C again to exit"
|
|
@@ -273857,6 +273858,7 @@ function Footer({
|
|
|
273857
273858
|
}, undefined, false, undefined, this) : /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
|
|
273858
273859
|
flexDirection: "row",
|
|
273859
273860
|
gap: 2,
|
|
273861
|
+
flexShrink: 0,
|
|
273860
273862
|
children: hotkeys.map((hotkey, index) => /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
|
|
273861
273863
|
flexDirection: "row",
|
|
273862
273864
|
gap: 1,
|
|
@@ -279246,7 +279248,9 @@ function AuthFlow({ onClose }) {
|
|
|
279246
279248
|
const { colors: colors2 } = useTheme();
|
|
279247
279249
|
const appConfig = useConfig();
|
|
279248
279250
|
const alreadyConnected = isConnected(appConfig.data);
|
|
279249
|
-
const
|
|
279251
|
+
const hasWorkspace = !!appConfig.data.workspaceId;
|
|
279252
|
+
const needsWorkspace = alreadyConnected && !hasWorkspace && !!appConfig.data.accessToken;
|
|
279253
|
+
const [step, setStep] = import_react48.useState(needsWorkspace ? "requesting" : alreadyConnected ? "success" : "start");
|
|
279250
279254
|
const [error40, setError] = import_react48.useState(null);
|
|
279251
279255
|
const [flowInfo, setFlowInfo] = import_react48.useState(null);
|
|
279252
279256
|
const [workspaces, setWorkspaces] = import_react48.useState([]);
|
|
@@ -279457,6 +279461,14 @@ function AuthFlow({ onClose }) {
|
|
|
279457
279461
|
setStep("error");
|
|
279458
279462
|
}
|
|
279459
279463
|
};
|
|
279464
|
+
import_react48.useEffect(() => {
|
|
279465
|
+
if (!needsWorkspace)
|
|
279466
|
+
return;
|
|
279467
|
+
const ac = new AbortController;
|
|
279468
|
+
abortRef.current = ac;
|
|
279469
|
+
const apiUrl = getPensarApiUrl();
|
|
279470
|
+
handleFetchWorkspaces(apiUrl, appConfig.data.accessToken, ac);
|
|
279471
|
+
}, []);
|
|
279460
279472
|
const handleDisconnect = async () => {
|
|
279461
279473
|
await disconnect();
|
|
279462
279474
|
appConfig.reload();
|
|
@@ -279948,7 +279960,11 @@ function ProviderManager() {
|
|
|
279948
279960
|
});
|
|
279949
279961
|
};
|
|
279950
279962
|
const handleAuthClose = () => {
|
|
279951
|
-
|
|
279963
|
+
if (isOnboarding) {
|
|
279964
|
+
setFlowState("choosing");
|
|
279965
|
+
} else {
|
|
279966
|
+
route.navigate({ type: "base", path: "home" });
|
|
279967
|
+
}
|
|
279952
279968
|
};
|
|
279953
279969
|
const otherProviders = AVAILABLE_PROVIDERS.filter((p) => p.id !== "pensar");
|
|
279954
279970
|
const selectedProviderInfo = AVAILABLE_PROVIDERS.find((p) => p.id === selectedProvider);
|
|
@@ -286757,7 +286773,23 @@ function MessageList({
|
|
|
286757
286773
|
}, undefined, false, undefined, this),
|
|
286758
286774
|
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
|
|
286759
286775
|
fg: colors2.textMuted,
|
|
286760
|
-
children:
|
|
286776
|
+
children: [
|
|
286777
|
+
" ",
|
|
286778
|
+
"- Switch between Plan or Default mode"
|
|
286779
|
+
]
|
|
286780
|
+
}, undefined, true, undefined, this)
|
|
286781
|
+
]
|
|
286782
|
+
}, undefined, true, undefined, this),
|
|
286783
|
+
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
|
|
286784
|
+
flexDirection: "row",
|
|
286785
|
+
children: [
|
|
286786
|
+
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
|
|
286787
|
+
fg: colors2.primary,
|
|
286788
|
+
children: "Option+Shift+Tab"
|
|
286789
|
+
}, undefined, false, undefined, this),
|
|
286790
|
+
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
|
|
286791
|
+
fg: colors2.textMuted,
|
|
286792
|
+
children: " - Toggle approval on/off"
|
|
286761
286793
|
}, undefined, false, undefined, this)
|
|
286762
286794
|
]
|
|
286763
286795
|
}, undefined, true, undefined, this)
|
|
@@ -286864,8 +286896,6 @@ function NormalInputAreaInner({
|
|
|
286864
286896
|
status,
|
|
286865
286897
|
mode = "operator",
|
|
286866
286898
|
operatorMode,
|
|
286867
|
-
verboseMode = false,
|
|
286868
|
-
expandedLogs = false,
|
|
286869
286899
|
enableAutocomplete = false,
|
|
286870
286900
|
autocompleteOptions = [],
|
|
286871
286901
|
enableCommands = false,
|
|
@@ -286944,30 +286974,25 @@ function NormalInputAreaInner({
|
|
|
286944
286974
|
gap: 2,
|
|
286945
286975
|
marginTop: 1,
|
|
286946
286976
|
backgroundColor: "transparent",
|
|
286977
|
+
alignItems: "center",
|
|
286978
|
+
overflow: "hidden",
|
|
286947
286979
|
children: [
|
|
286948
|
-
|
|
286949
|
-
fg:
|
|
286950
|
-
|
|
286951
|
-
|
|
286952
|
-
operatorMode === "auto" && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
|
|
286953
|
-
fg: colors2.primary,
|
|
286954
|
-
children: "AUTO"
|
|
286955
|
-
}, undefined, false, undefined, this),
|
|
286956
|
-
operatorMode === "manual" && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
|
|
286957
|
-
fg: colors2.textMuted,
|
|
286958
|
-
children: "MANUAL"
|
|
286980
|
+
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
|
|
286981
|
+
fg: "#000000",
|
|
286982
|
+
bg: operatorMode === "plan" ? colors2.warning : colors2.success,
|
|
286983
|
+
children: ` ${operatorMode === "plan" ? "PLAN" : "DEFAULT"} `
|
|
286959
286984
|
}, undefined, false, undefined, this),
|
|
286960
286985
|
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
|
|
286961
286986
|
fg: colors2.textMuted,
|
|
286962
286987
|
children: "/ commands"
|
|
286963
286988
|
}, undefined, false, undefined, this),
|
|
286964
286989
|
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
|
|
286965
|
-
fg:
|
|
286966
|
-
children:
|
|
286990
|
+
fg: colors2.textMuted,
|
|
286991
|
+
children: "⇧Tab mode"
|
|
286967
286992
|
}, undefined, false, undefined, this),
|
|
286968
286993
|
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
|
|
286969
|
-
fg:
|
|
286970
|
-
children:
|
|
286994
|
+
fg: colors2.textMuted,
|
|
286995
|
+
children: "⌥⇧Tab approval"
|
|
286971
286996
|
}, undefined, false, undefined, this),
|
|
286972
286997
|
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
|
|
286973
286998
|
fg: colors2.textMuted,
|
|
@@ -288417,7 +288442,7 @@ function OperatorDashboard({
|
|
|
288417
288442
|
children: agentMode === "plan" ? "PLAN" : "DEFAULT"
|
|
288418
288443
|
}, undefined, false, undefined, this),
|
|
288419
288444
|
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
|
|
288420
|
-
fg: operatorState.requireApproval ? colors2.
|
|
288445
|
+
fg: operatorState.requireApproval ? colors2.success : colors2.warning,
|
|
288421
288446
|
children: operatorState.requireApproval ? "APPROVAL ON" : "APPROVAL OFF"
|
|
288422
288447
|
}, undefined, false, undefined, this),
|
|
288423
288448
|
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
|
|
@@ -288459,9 +288484,7 @@ function OperatorDashboard({
|
|
|
288459
288484
|
focused: status === "running" ? selectedQueueIndex < 0 : resolveInputFocused(status, stack.length, externalDialogOpen),
|
|
288460
288485
|
status: status === "waiting" ? "running" : status,
|
|
288461
288486
|
mode: "operator",
|
|
288462
|
-
operatorMode: agentMode
|
|
288463
|
-
verboseMode,
|
|
288464
|
-
expandedLogs,
|
|
288487
|
+
operatorMode: agentMode,
|
|
288465
288488
|
pendingApproval: currentPending,
|
|
288466
288489
|
onApprove: handleApprove,
|
|
288467
288490
|
onAutoApprove: handleAutoApprove,
|
|
@@ -291750,8 +291773,8 @@ function AppContent({
|
|
|
291750
291773
|
return;
|
|
291751
291774
|
if (!config3.data.responsibleUseAccepted && route.data.path !== "disclosure") {
|
|
291752
291775
|
route.navigate({ type: "base", path: "disclosure" });
|
|
291753
|
-
} else if (config3.data.responsibleUseAccepted && !hasAnyProviderConfigured(config3.data) && route.data.path !== "providers" && route.data.path !== "disclosure") {
|
|
291754
|
-
route.navigate({ type: "base", path: "
|
|
291776
|
+
} else if (config3.data.responsibleUseAccepted && !hasAnyProviderConfigured(config3.data) && route.data.path !== "auth" && route.data.path !== "providers" && route.data.path !== "disclosure") {
|
|
291777
|
+
route.navigate({ type: "base", path: "auth" });
|
|
291755
291778
|
}
|
|
291756
291779
|
}, [config3.data.responsibleUseAccepted, route.data]);
|
|
291757
291780
|
import_react90.useEffect(() => {
|
|
@@ -291879,7 +291902,7 @@ function CommandDisplay({
|
|
|
291879
291902
|
await config3.update({ responsibleUseAccepted: true });
|
|
291880
291903
|
route.navigate({
|
|
291881
291904
|
type: "base",
|
|
291882
|
-
path: "
|
|
291905
|
+
path: "auth"
|
|
291883
291906
|
});
|
|
291884
291907
|
};
|
|
291885
291908
|
if (route.data.type === "base") {
|
|
@@ -291925,6 +291948,18 @@ function CommandDisplay({
|
|
|
291925
291948
|
when: "models",
|
|
291926
291949
|
children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(ModelsDisplay, {}, undefined, false, undefined, this)
|
|
291927
291950
|
}, undefined, false, undefined, this),
|
|
291951
|
+
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(RouteSwitch.Case, {
|
|
291952
|
+
when: "auth",
|
|
291953
|
+
children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(AuthFlow, {
|
|
291954
|
+
onClose: () => {
|
|
291955
|
+
if (hasAnyProviderConfigured(config3.data)) {
|
|
291956
|
+
route.navigate({ type: "base", path: "home" });
|
|
291957
|
+
} else {
|
|
291958
|
+
route.navigate({ type: "base", path: "providers" });
|
|
291959
|
+
}
|
|
291960
|
+
}
|
|
291961
|
+
}, undefined, false, undefined, this)
|
|
291962
|
+
}, undefined, false, undefined, this),
|
|
291928
291963
|
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(RouteSwitch.Case, {
|
|
291929
291964
|
when: "providers",
|
|
291930
291965
|
children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(ProviderManager, {}, undefined, false, undefined, this)
|