@mcp-shark/mcp-shark 1.5.13 → 1.7.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.
Files changed (158) hide show
  1. package/README.md +482 -56
  2. package/bin/mcp-shark.js +146 -52
  3. package/core/cli/AutoFixEngine.js +93 -0
  4. package/core/cli/ConfigScanner.js +193 -0
  5. package/core/cli/DataLoader.js +200 -0
  6. package/core/cli/DeclarativeRuleEngine.js +363 -0
  7. package/core/cli/DoctorCommand.js +218 -0
  8. package/core/cli/FixHandlers.js +222 -0
  9. package/core/cli/HtmlReportGenerator.js +203 -0
  10. package/core/cli/IdeConfigPaths.js +175 -0
  11. package/core/cli/ListCommand.js +255 -0
  12. package/core/cli/LockCommand.js +164 -0
  13. package/core/cli/LockDiffEngine.js +152 -0
  14. package/core/cli/RuleRegistryConfig.js +131 -0
  15. package/core/cli/ScanCommand.js +244 -0
  16. package/core/cli/ScanService.js +200 -0
  17. package/core/cli/SecretDetector.js +92 -0
  18. package/core/cli/SharkScoreCalculator.js +109 -0
  19. package/core/cli/ToolClassifications.js +51 -0
  20. package/core/cli/ToxicFlowAnalyzer.js +212 -0
  21. package/core/cli/UpdateCommand.js +188 -0
  22. package/core/cli/WalkthroughGenerator.js +195 -0
  23. package/core/cli/WatchCommand.js +129 -0
  24. package/core/cli/YamlRuleEngine.js +197 -0
  25. package/core/cli/data/rule-packs/aauth-visibility.json +117 -0
  26. package/core/cli/data/rule-packs/agentic-security-2026.json +180 -0
  27. package/core/cli/data/rule-packs/general-security.json +173 -0
  28. package/core/cli/data/rule-packs/owasp-mcp-2026.json +244 -0
  29. package/core/cli/data/rule-packs/toxic-flow-heuristics.json +21 -0
  30. package/core/cli/data/rule-sources.json +5 -0
  31. package/core/cli/data/secret-patterns.json +18 -0
  32. package/core/cli/data/tool-classifications.json +111 -0
  33. package/core/cli/data/toxic-flow-rules.json +47 -0
  34. package/core/cli/index.js +23 -0
  35. package/core/cli/output/Banner.js +52 -0
  36. package/core/cli/output/Formatter.js +183 -0
  37. package/core/cli/output/JsonFormatter.js +106 -0
  38. package/core/cli/output/index.js +16 -0
  39. package/core/cli/secureRegistryFetch.js +157 -0
  40. package/core/cli/symbols.js +16 -0
  41. package/core/configs/environment.js +3 -1
  42. package/core/configs/index.js +3 -64
  43. package/core/container/DependencyContainer.js +4 -1
  44. package/core/mcp-server/index.js +4 -1
  45. package/core/mcp-server/server/external/all.js +10 -3
  46. package/core/mcp-server/server/external/config.js +62 -5
  47. package/core/models/RequestFilters.js +3 -0
  48. package/core/repositories/PacketRepository.js +16 -0
  49. package/core/services/AuditService.js +2 -0
  50. package/core/services/ConfigService.js +9 -1
  51. package/core/services/ConfigTransformService.js +34 -2
  52. package/core/services/RequestService.js +58 -5
  53. package/core/services/ServerManagementService.js +59 -4
  54. package/core/services/security/StaticRulesService.js +69 -13
  55. package/core/services/security/TrafficAnalysisService.js +19 -1
  56. package/core/services/security/TrafficToxicFlowService.js +154 -0
  57. package/core/services/security/aauthGraph.js +199 -0
  58. package/core/services/security/aauthParser.js +274 -0
  59. package/core/services/security/aauthSelfTest.js +346 -0
  60. package/core/services/security/index.js +2 -1
  61. package/core/services/security/rules/index.js +25 -59
  62. package/core/services/security/rules/scans/configPermissions.js +91 -0
  63. package/core/services/security/rules/scans/duplicateToolNames.js +85 -0
  64. package/core/services/security/rules/scans/insecureTransport.js +148 -0
  65. package/core/services/security/rules/scans/missingContainment.js +123 -0
  66. package/core/services/security/rules/scans/shellEnvInjection.js +101 -0
  67. package/core/services/security/rules/scans/unsafeDefaults.js +99 -0
  68. package/core/services/security/toolsListFromTrafficParser.js +70 -0
  69. package/core/tui/App.js +144 -0
  70. package/core/tui/FindingsPanel.js +115 -0
  71. package/core/tui/FixPanel.js +132 -0
  72. package/core/tui/Header.js +51 -0
  73. package/core/tui/HelpBar.js +42 -0
  74. package/core/tui/ServersPanel.js +109 -0
  75. package/core/tui/ToxicFlowsPanel.js +100 -0
  76. package/core/tui/h.js +8 -0
  77. package/core/tui/index.js +11 -0
  78. package/core/tui/render.js +22 -0
  79. package/package.json +24 -16
  80. package/ui/dist/assets/index-D6zDrtMV.js +81 -0
  81. package/ui/dist/index.html +1 -1
  82. package/ui/server/controllers/AauthController.js +279 -0
  83. package/ui/server/controllers/RequestController.js +12 -1
  84. package/ui/server/controllers/SecurityFindingsController.js +46 -1
  85. package/ui/server/routes/aauth.js +18 -0
  86. package/ui/server/routes/requests.js +8 -1
  87. package/ui/server/routes/security.js +5 -1
  88. package/ui/server/setup.js +224 -6
  89. package/ui/server/swagger/paths/components.js +55 -0
  90. package/ui/server/swagger/paths/securityTrafficFlows.js +59 -0
  91. package/ui/server/swagger/paths.js +2 -2
  92. package/ui/server/swagger/swagger.js +5 -2
  93. package/ui/server.js +1 -1
  94. package/ui/src/App.jsx +26 -52
  95. package/ui/src/PacketFilters.jsx +31 -1
  96. package/ui/src/PacketList.jsx +2 -2
  97. package/ui/src/Security.jsx +10 -0
  98. package/ui/src/TabNavigation.jsx +8 -0
  99. package/ui/src/components/AAuthBadge.jsx +92 -0
  100. package/ui/src/components/AauthExplorer/AauthExplorerGraph.jsx +231 -0
  101. package/ui/src/components/AauthExplorer/AauthExplorerView.jsx +387 -0
  102. package/ui/src/components/AauthExplorer/NodeDetailPanel.jsx +272 -0
  103. package/ui/src/components/App/ActionMenu.jsx +4 -31
  104. package/ui/src/components/App/ApiDocsButton.jsx +0 -1
  105. package/ui/src/components/App/ShutdownButton.jsx +0 -1
  106. package/ui/src/components/App/useAppState.js +19 -26
  107. package/ui/src/components/DetailsTab/AAuthIdentitySection.jsx +119 -0
  108. package/ui/src/components/DetailsTab/RequestDetailsSection.jsx +2 -0
  109. package/ui/src/components/DetailsTab/ResponseDetailsSection.jsx +2 -0
  110. package/ui/src/components/DetectedPathsList.jsx +1 -5
  111. package/ui/src/components/FileInput.jsx +0 -1
  112. package/ui/src/components/PacketFilters/AAuthPostureFilter.jsx +81 -0
  113. package/ui/src/components/RequestRow/RequestRowMain.jsx +7 -1
  114. package/ui/src/components/Security/AAuthPosturePanel.jsx +360 -0
  115. package/ui/src/components/Security/ScannerContent.jsx +33 -1
  116. package/ui/src/components/Security/TrafficToxicFlowsPanel.jsx +253 -0
  117. package/ui/src/components/Security/securityApi.js +15 -0
  118. package/ui/src/components/Security/useSecurity.js +60 -3
  119. package/ui/src/components/ServerControl.jsx +0 -1
  120. package/ui/src/components/TabNavigation/DesktopTabs.jsx +0 -11
  121. package/ui/src/components/TabNavigationIcons.jsx +5 -0
  122. package/ui/src/components/ViewModeTabs.jsx +0 -1
  123. package/ui/src/utils/animations.js +26 -9
  124. package/core/services/security/rules/scans/agentic01GoalHijack.js +0 -130
  125. package/core/services/security/rules/scans/agentic02ToolMisuse.js +0 -129
  126. package/core/services/security/rules/scans/agentic03IdentityAbuse.js +0 -130
  127. package/core/services/security/rules/scans/agentic04SupplyChain.js +0 -130
  128. package/core/services/security/rules/scans/agentic06MemoryPoisoning.js +0 -130
  129. package/core/services/security/rules/scans/agentic07InsecureCommunication.js +0 -135
  130. package/core/services/security/rules/scans/agentic08CascadingFailures.js +0 -135
  131. package/core/services/security/rules/scans/agentic09TrustExploitation.js +0 -135
  132. package/core/services/security/rules/scans/agentic10RogueAgent.js +0 -130
  133. package/core/services/security/rules/scans/hardcodedSecrets.js +0 -130
  134. package/core/services/security/rules/scans/mcp01TokenMismanagement.js +0 -127
  135. package/core/services/security/rules/scans/mcp02ScopeCreep.js +0 -130
  136. package/core/services/security/rules/scans/mcp03ToolPoisoning.js +0 -132
  137. package/core/services/security/rules/scans/mcp04SupplyChain.js +0 -131
  138. package/core/services/security/rules/scans/mcp06PromptInjection.js +0 -200
  139. package/core/services/security/rules/scans/mcp07InsufficientAuth.js +0 -130
  140. package/core/services/security/rules/scans/mcp08LackAudit.js +0 -129
  141. package/core/services/security/rules/scans/mcp09ShadowServers.js +0 -129
  142. package/core/services/security/rules/scans/mcp10ContextInjection.js +0 -130
  143. package/ui/dist/assets/index-CiCSDYf-.js +0 -97
  144. package/ui/server/routes/help.js +0 -44
  145. package/ui/server/swagger/paths/help.js +0 -82
  146. package/ui/src/HelpGuide/HelpGuideContent.jsx +0 -118
  147. package/ui/src/HelpGuide/HelpGuideFooter.jsx +0 -59
  148. package/ui/src/HelpGuide/HelpGuideHeader.jsx +0 -57
  149. package/ui/src/HelpGuide.jsx +0 -78
  150. package/ui/src/IntroTour.jsx +0 -154
  151. package/ui/src/components/App/HelpButton.jsx +0 -90
  152. package/ui/src/components/TourOverlay.jsx +0 -117
  153. package/ui/src/components/TourTooltip/TourTooltipButtons.jsx +0 -120
  154. package/ui/src/components/TourTooltip/TourTooltipHeader.jsx +0 -71
  155. package/ui/src/components/TourTooltip/TourTooltipIcons.jsx +0 -54
  156. package/ui/src/components/TourTooltip/useTooltipPosition.js +0 -135
  157. package/ui/src/components/TourTooltip.jsx +0 -91
  158. package/ui/src/config/tourSteps.jsx +0 -140
@@ -2,13 +2,12 @@ import { IconMenu2, IconX } from '@tabler/icons-react';
2
2
  import { useEffect, useRef, useState } from 'react';
3
3
  import { colors, fonts } from '../../theme';
4
4
  import ApiDocsButton from './ApiDocsButton';
5
- import HelpButton from './HelpButton';
6
5
  import ShutdownButton from './ShutdownButton';
7
6
 
8
7
  /**
9
- * Expandable action menu grouping API, Help, and Shutdown buttons
8
+ * Expandable action menu grouping API docs and shutdown
10
9
  */
11
- export default function ActionMenu({ onHelpClick }) {
10
+ export default function ActionMenu() {
12
11
  const [isExpanded, setIsExpanded] = useState(false);
13
12
  const menuRef = useRef(null);
14
13
 
@@ -73,39 +72,13 @@ export default function ActionMenu({ onHelpClick }) {
73
72
  <div ref={menuRef} style={{ position: 'fixed', bottom: '20px', right: '20px', zIndex: 9999 }}>
74
73
  {/* API Docs Button - Top */}
75
74
  <ApiDocsButton
76
- style={{
77
- ...menuItemStyle,
78
- bottom: isExpanded ? '180px' : '0',
79
- position: 'absolute',
80
- left: 'auto', // Ensure no left positioning conflicts
81
- }}
82
- onClick={() => setIsExpanded(false)}
83
- onMouseEnter={(e) => {
84
- if (isExpanded) {
85
- e.currentTarget.style.transform = 'scale(1.1) translateY(0)';
86
- e.currentTarget.style.boxShadow = `0 6px 16px ${colors.shadowLg}`;
87
- }
88
- }}
89
- onMouseLeave={(e) => {
90
- if (isExpanded) {
91
- e.currentTarget.style.transform = 'scale(1) translateY(0)';
92
- e.currentTarget.style.boxShadow = `0 4px 12px ${colors.shadowMd}`;
93
- }
94
- }}
95
- />
96
-
97
- {/* Help Button - Middle */}
98
- <HelpButton
99
- onClick={() => {
100
- onHelpClick();
101
- setIsExpanded(false);
102
- }}
103
75
  style={{
104
76
  ...menuItemStyle,
105
77
  bottom: isExpanded ? '120px' : '0',
106
78
  position: 'absolute',
107
79
  left: 'auto', // Ensure no left positioning conflicts
108
80
  }}
81
+ onClick={() => setIsExpanded(false)}
109
82
  onMouseEnter={(e) => {
110
83
  if (isExpanded) {
111
84
  e.currentTarget.style.transform = 'scale(1.1) translateY(0)';
@@ -120,7 +93,7 @@ export default function ActionMenu({ onHelpClick }) {
120
93
  }}
121
94
  />
122
95
 
123
- {/* Shutdown Button - Bottom (closest to main button) */}
96
+ {/* Shutdown Button closest to main toggle */}
124
97
  <ShutdownButton
125
98
  style={{
126
99
  ...menuItemStyle,
@@ -66,7 +66,6 @@ export default function ApiDocsButton({ style, onClick, onMouseEnter, onMouseLea
66
66
  <button
67
67
  type="button"
68
68
  onClick={handleClick}
69
- data-tour="api-docs-button"
70
69
  style={defaultStyle}
71
70
  onMouseEnter={handleMouseEnter}
72
71
  onMouseLeave={handleMouseLeave}
@@ -90,7 +90,6 @@ export default function ShutdownButton({ style, onClick, onMouseEnter, onMouseLe
90
90
  <button
91
91
  type="button"
92
92
  onClick={handleClick}
93
- data-tour="shutdown-button"
94
93
  style={defaultStyle}
95
94
  onMouseEnter={handleMouseEnter}
96
95
  onMouseLeave={handleMouseLeave}
@@ -22,9 +22,27 @@ function appendFilterParams(queryParams, filters) {
22
22
  if (filters.jsonrpcId) {
23
23
  queryParams.append('jsonrpcId', filters.jsonrpcId);
24
24
  }
25
+ if (filters.aauthPosture) {
26
+ queryParams.append('aauthPosture', filters.aauthPosture);
27
+ }
28
+ if (filters.aauthAgent) {
29
+ queryParams.append('aauthAgent', filters.aauthAgent);
30
+ }
31
+ if (filters.aauthMission) {
32
+ queryParams.append('aauthMission', filters.aauthMission);
33
+ }
25
34
  }
26
35
 
27
- const VALID_TABS = ['traffic', 'logs', 'setup', 'playground', 'smart-scan'];
36
+ const VALID_TABS = [
37
+ 'traffic',
38
+ 'logs',
39
+ 'setup',
40
+ 'playground',
41
+ 'security',
42
+ 'aauth-explorer',
43
+ 'smart-scan',
44
+ 'shutdown',
45
+ ];
28
46
  const DEFAULT_TAB = 'traffic';
29
47
 
30
48
  function getTabFromHash() {
@@ -47,8 +65,6 @@ export function useAppState() {
47
65
  const [filters, setFilters] = useState({});
48
66
  const [stats, setStats] = useState(null);
49
67
  const [firstRequestTime, setFirstRequestTime] = useState(null);
50
- const [showTour, setShowTour] = useState(false);
51
- const [tourDismissed, setTourDismissed] = useState(true);
52
68
  const wsRef = useRef(null);
53
69
  const prevTabRef = useRef(activeTab);
54
70
  const filtersRef = useRef(filters);
@@ -117,25 +133,6 @@ export function useAppState() {
117
133
  };
118
134
 
119
135
  useEffect(() => {
120
- const checkTourState = async () => {
121
- try {
122
- const response = await fetch('/api/help/state');
123
- const data = await response.json();
124
- setTourDismissed(data.dismissed || data.tourCompleted);
125
- if (!data.dismissed && !data.tourCompleted) {
126
- setTimeout(() => {
127
- setShowTour(true);
128
- }, 500);
129
- }
130
- } catch (error) {
131
- console.error('Failed to load tour state:', error);
132
- setTimeout(() => {
133
- setShowTour(true);
134
- }, 500);
135
- setTourDismissed(false);
136
- }
137
- };
138
-
139
136
  const initData = async () => {
140
137
  try {
141
138
  const queryParams = new URLSearchParams();
@@ -165,7 +162,6 @@ export function useAppState() {
165
162
  }
166
163
  };
167
164
 
168
- checkTourState();
169
165
  initData();
170
166
 
171
167
  const wsUrl = import.meta.env.DEV
@@ -289,9 +285,6 @@ export function useAppState() {
289
285
  setFilters,
290
286
  stats,
291
287
  firstRequestTime,
292
- showTour,
293
- setShowTour,
294
- tourDismissed,
295
288
  prevTabRef,
296
289
  wsRef,
297
290
  loadRequests,
@@ -0,0 +1,119 @@
1
+ import { colors, fonts } from '../../theme';
2
+ import AAuthBadge from '../AAuthBadge';
3
+ import CollapsibleSection from '../CollapsibleSection';
4
+
5
+ function Field({ label, value, mono = false }) {
6
+ if (!value) {
7
+ return null;
8
+ }
9
+ return (
10
+ <div style={{ display: 'flex', gap: '12px', padding: '4px 0' }}>
11
+ <div
12
+ style={{
13
+ minWidth: '140px',
14
+ color: colors.textSecondary,
15
+ fontSize: '11px',
16
+ fontFamily: fonts.body,
17
+ fontWeight: 500,
18
+ textTransform: 'uppercase',
19
+ letterSpacing: '0.04em',
20
+ }}
21
+ >
22
+ {label}
23
+ </div>
24
+ <div
25
+ style={{
26
+ color: colors.textPrimary,
27
+ fontSize: '12px',
28
+ fontFamily: mono ? fonts.mono : fonts.body,
29
+ wordBreak: 'break-all',
30
+ }}
31
+ >
32
+ {value}
33
+ </div>
34
+ </div>
35
+ );
36
+ }
37
+
38
+ /**
39
+ * Renders an AAuth Identity panel for a single packet (request or response).
40
+ * Always read-only and observation-only — values describe what was on the
41
+ * wire, never claims of cryptographic validity.
42
+ */
43
+ export default function AAuthIdentitySection({ aauth, titleColor }) {
44
+ if (!aauth) {
45
+ return null;
46
+ }
47
+
48
+ const hasAnySignal =
49
+ aauth.posture !== 'none' ||
50
+ aauth.agent ||
51
+ aauth.mission ||
52
+ aauth.requirement ||
53
+ aauth.sig_present ||
54
+ aauth.error;
55
+
56
+ if (!hasAnySignal) {
57
+ return null;
58
+ }
59
+
60
+ const coveredText =
61
+ Array.isArray(aauth.sig_covered) && aauth.sig_covered.length > 0
62
+ ? aauth.sig_covered.join(', ')
63
+ : null;
64
+
65
+ return (
66
+ <CollapsibleSection
67
+ title={
68
+ <span style={{ display: 'inline-flex', alignItems: 'center', gap: '8px' }}>
69
+ Identity
70
+ <AAuthBadge aauth={aauth} size="sm" />
71
+ </span>
72
+ }
73
+ titleColor={titleColor || colors.accentBlue}
74
+ defaultExpanded={true}
75
+ >
76
+ <div
77
+ style={{
78
+ padding: '12px 14px',
79
+ background: colors.bgSecondary,
80
+ border: `1px solid ${colors.borderLight}`,
81
+ borderRadius: '8px',
82
+ }}
83
+ >
84
+ <Field label="Agent" value={aauth.agent} mono />
85
+ <Field label="Mission" value={aauth.mission} mono />
86
+ <Field label="Signature" value={aauth.sig_present ? 'present (not verified)' : null} />
87
+ <Field label="Algorithm" value={aauth.sig_alg} mono />
88
+ <Field label="Key ID" value={aauth.sig_keyid_short || aauth.sig_keyid} mono />
89
+ <Field label="Key thumbprint" value={aauth.key_thumbprint_short} mono />
90
+ <Field label="Covered components" value={coveredText} mono />
91
+ <Field label="Requirement" value={aauth.requirement} />
92
+ <Field label="Signature error" value={aauth.error} />
93
+ <div
94
+ style={{
95
+ marginTop: '10px',
96
+ paddingTop: '8px',
97
+ borderTop: `1px dashed ${colors.borderLight}`,
98
+ color: colors.textTertiary,
99
+ fontSize: '11px',
100
+ fontFamily: fonts.body,
101
+ lineHeight: 1.45,
102
+ }}
103
+ >
104
+ mcp-shark records AAuth signals as observed only. No signatures are cryptographically
105
+ verified. Learn more at{' '}
106
+ <a
107
+ href="https://www.aauth.dev"
108
+ target="_blank"
109
+ rel="noreferrer noopener"
110
+ style={{ color: colors.accentBlue }}
111
+ >
112
+ aauth.dev
113
+ </a>
114
+ .
115
+ </div>
116
+ </div>
117
+ </CollapsibleSection>
118
+ );
119
+ }
@@ -1,5 +1,6 @@
1
1
  import { colors } from '../../theme';
2
2
  import CollapsibleSection from '../CollapsibleSection';
3
+ import AAuthIdentitySection from './AAuthIdentitySection';
3
4
  import BodySection from './BodySection';
4
5
  import CollapsibleRequestResponse from './CollapsibleRequestResponse';
5
6
  import HeadersSection from './HeadersSection';
@@ -21,6 +22,7 @@ export default function RequestDetailsSection({ request, requestHeaders, request
21
22
  <InfoSection data={request} titleColor={colors.accentBlue} />
22
23
  <NetworkInfoSection data={request} showUserAgent={true} />
23
24
  <ProtocolInfoSection data={request} titleColor={colors.accentBlue} />
25
+ <AAuthIdentitySection aauth={request.aauth} titleColor={colors.accentBlue} />
24
26
  <HeadersSection headers={requestHeaders} titleColor={colors.accentBlue} />
25
27
  <BodySection body={requestBody} titleColor={colors.accentBlue} />
26
28
  {request.jsonrpc_params && (
@@ -1,5 +1,6 @@
1
1
  import { colors, fonts } from '../../theme';
2
2
  import CollapsibleSection from '../CollapsibleSection';
3
+ import AAuthIdentitySection from './AAuthIdentitySection';
3
4
  import BodySection from './BodySection';
4
5
  import CollapsibleRequestResponse from './CollapsibleRequestResponse';
5
6
  import HeadersSection from './HeadersSection';
@@ -21,6 +22,7 @@ export default function ResponseDetailsSection({ response, responseHeaders, resp
21
22
  <InfoSection data={response} titleColor={colors.accentGreen} />
22
23
  <NetworkInfoSection data={response} />
23
24
  <ProtocolInfoSection data={response} titleColor={colors.accentGreen} />
25
+ <AAuthIdentitySection aauth={response.aauth} titleColor={colors.accentGreen} />
24
26
  <HeadersSection headers={responseHeaders} titleColor={colors.accentGreen} />
25
27
  <BodySection body={responseBody} titleColor={colors.accentGreen} />
26
28
  {response.jsonrpc_result && (
@@ -62,15 +62,11 @@ function DetectedPathsList({ detectedPaths, detecting, onDetect, onSelect, onVie
62
62
  )}
63
63
  </button>
64
64
  </div>
65
- <div
66
- data-tour="detected-editors"
67
- style={{ display: 'flex', flexDirection: 'column', gap: '6px' }}
68
- >
65
+ <div style={{ display: 'flex', flexDirection: 'column', gap: '6px' }}>
69
66
  {detectedPaths.map((item, idx) => (
70
67
  <button
71
68
  key={`${item.editor}-${item.path}-${idx}`}
72
69
  type="button"
73
- data-tour={idx === 0 ? 'first-detected-editor' : undefined}
74
70
  onClick={() => onSelect(item.path)}
75
71
  onDoubleClick={() => {
76
72
  if (item.exists) {
@@ -13,7 +13,6 @@ function FileInput({
13
13
  <>
14
14
  <div style={{ display: 'flex', gap: '8px', alignItems: 'center' }}>
15
15
  <label
16
- data-tour="select-file"
17
16
  style={{
18
17
  padding: '8px 16px',
19
18
  background: colors.buttonPrimary,
@@ -0,0 +1,81 @@
1
+ import { colors, fonts } from '../../theme';
2
+
3
+ const POSTURES = [
4
+ { id: null, label: 'All', dotColor: colors.textTertiary },
5
+ { id: 'signed', label: 'Signed', dotColor: colors.success },
6
+ { id: 'aauth-aware', label: 'AAuth-aware', dotColor: colors.accentBlue },
7
+ { id: 'bearer', label: 'Bearer', dotColor: colors.warning },
8
+ { id: 'none', label: 'No auth', dotColor: colors.textTertiary },
9
+ ];
10
+
11
+ /**
12
+ * Segmented selector that filters captured traffic by AAuth posture.
13
+ * Visualization-only — does not change the underlying traffic in any way.
14
+ */
15
+ export default function AAuthPostureFilter({ value, onChange }) {
16
+ return (
17
+ <div
18
+ title="Filter packets by observed AAuth posture (no verification)"
19
+ style={{
20
+ display: 'inline-flex',
21
+ alignItems: 'center',
22
+ gap: 0,
23
+ background: colors.bgPrimary,
24
+ border: `1px solid ${colors.borderLight}`,
25
+ borderRadius: '999px',
26
+ padding: '2px',
27
+ height: '34px',
28
+ }}
29
+ >
30
+ <span
31
+ style={{
32
+ fontSize: '10px',
33
+ fontFamily: fonts.body,
34
+ color: colors.textTertiary,
35
+ textTransform: 'uppercase',
36
+ letterSpacing: '0.05em',
37
+ padding: '0 8px 0 10px',
38
+ }}
39
+ >
40
+ AAuth
41
+ </span>
42
+ {POSTURES.map((option) => {
43
+ const active = (value || null) === (option.id || null);
44
+ return (
45
+ <button
46
+ key={option.label}
47
+ type="button"
48
+ onClick={() => onChange(option.id)}
49
+ style={{
50
+ display: 'inline-flex',
51
+ alignItems: 'center',
52
+ gap: '6px',
53
+ padding: '4px 10px',
54
+ border: 'none',
55
+ cursor: 'pointer',
56
+ fontSize: '11px',
57
+ fontFamily: fonts.body,
58
+ fontWeight: active ? 600 : 500,
59
+ color: active ? colors.textPrimary : colors.textSecondary,
60
+ background: active ? colors.bgSelected : 'transparent',
61
+ borderRadius: '999px',
62
+ transition: 'background-color 0.15s ease',
63
+ }}
64
+ >
65
+ <span
66
+ aria-hidden="true"
67
+ style={{
68
+ width: '6px',
69
+ height: '6px',
70
+ borderRadius: '50%',
71
+ background: option.dotColor,
72
+ display: 'inline-block',
73
+ }}
74
+ />
75
+ {option.label}
76
+ </button>
77
+ );
78
+ })}
79
+ </div>
80
+ );
81
+ }
@@ -6,6 +6,7 @@ import {
6
6
  getEndpoint,
7
7
  getSourceDest,
8
8
  } from '../../utils/requestUtils.js';
9
+ import AAuthBadge from '../AAuthBadge';
9
10
 
10
11
  const ChevronDown = ({ size = 12, rotated = false }) => (
11
12
  <IconChevronDown
@@ -182,7 +183,12 @@ export default function RequestRowMain({
182
183
  fontWeight: '500',
183
184
  }}
184
185
  >
185
- {dest}
186
+ <div style={{ display: 'flex', alignItems: 'center', gap: '8px', flexWrap: 'wrap' }}>
187
+ <span>{dest}</span>
188
+ {request.aauth && request.aauth.posture !== 'none' && (
189
+ <AAuthBadge aauth={request.aauth} size="sm" showLabel={false} />
190
+ )}
191
+ </div>
186
192
  </td>
187
193
  <td
188
194
  style={{