@skyhook-io/k8s-ui 1.6.0 → 1.6.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/package.json +7 -2
- package/src/components/charts/AreaChart.tsx +371 -0
- package/src/components/charts/MetricsSummary.tsx +49 -0
- package/src/components/charts/SeriesLegend.tsx +27 -0
- package/src/components/charts/colors.ts +49 -0
- package/src/components/charts/format.ts +41 -0
- package/src/components/charts/index.ts +14 -0
- package/src/components/charts/saturation.test.ts +60 -0
- package/src/components/charts/saturation.ts +32 -0
- package/src/components/charts/types.ts +36 -0
- package/src/components/compare/CompareResourcePicker.tsx +192 -0
- package/src/components/compare/CompareTray.tsx +130 -0
- package/src/components/compare/ResourceCompareView.tsx +272 -0
- package/src/components/compare/index.ts +14 -0
- package/src/components/compare/normalize.test.ts +193 -0
- package/src/components/compare/normalize.ts +69 -0
- package/src/components/compare/picks.test.ts +97 -0
- package/src/components/compare/picks.ts +35 -0
- package/src/components/compare/sort.test.ts +78 -0
- package/src/components/compare/sort.ts +26 -0
- package/src/components/compare/types.ts +43 -0
- package/src/components/compare/url.test.ts +61 -0
- package/src/components/compare/url.ts +26 -0
- package/src/components/gitops/GitOpsDetailLayout.tsx +621 -0
- package/src/components/gitops/GitOpsGraphFilterRail.tsx +216 -0
- package/src/components/gitops/GitOpsTableView.tsx +1441 -0
- package/src/components/gitops/RollbackDialog.tsx +117 -0
- package/src/components/gitops/SyncOptionsDialog.tsx +160 -0
- package/src/components/gitops/detail-helpers.test.ts +97 -0
- package/src/components/gitops/detail-helpers.ts +112 -0
- package/src/components/gitops/index.ts +52 -1
- package/src/components/gitops/short-cluster-name.test.ts +36 -0
- package/src/components/gitops/tree/index.ts +2 -0
- package/src/components/gitops/tree/merge.test.ts +240 -0
- package/src/components/gitops/tree/merge.ts +160 -0
- package/src/components/resources/ResourcesSidebar.tsx +42 -15
- package/src/components/resources/ResourcesView.tsx +403 -69
- package/src/components/resources/index.ts +1 -0
- package/src/components/resources/renderers/CompositeRenderer.tsx +233 -0
- package/src/components/resources/renderers/CompositionRenderer.tsx +218 -0
- package/src/components/resources/renderers/CrossplanePackageRenderer.tsx +145 -0
- package/src/components/resources/renderers/CrossplaneProviderConfigRenderer.tsx +71 -0
- package/src/components/resources/renderers/HPARenderer.tsx +6 -1
- package/src/components/resources/renderers/ManagedResourceRenderer.tsx +131 -0
- package/src/components/resources/renderers/NamespaceRenderer.tsx +223 -0
- package/src/components/resources/renderers/PVCRenderer.tsx +6 -1
- package/src/components/resources/renderers/PodRenderer.tsx +207 -2
- package/src/components/resources/renderers/RoleBindingRenderer.tsx +132 -20
- package/src/components/resources/renderers/RoleRenderer.tsx +148 -18
- package/src/components/resources/renderers/ServiceAccountRenderer.tsx +456 -3
- package/src/components/resources/renderers/WorkloadRenderer.tsx +189 -2
- package/src/components/resources/renderers/XRDRenderer.tsx +153 -0
- package/src/components/resources/renderers/crossplane-cells.tsx +146 -0
- package/src/components/resources/renderers/flux-cells.tsx +12 -0
- package/src/components/resources/renderers/index.ts +8 -0
- package/src/components/resources/resource-utils-crossplane.test.ts +609 -0
- package/src/components/resources/resource-utils-crossplane.ts +325 -0
- package/src/components/resources/resource-utils-flux.ts +14 -0
- package/src/components/resources/resource-utils.ts +1 -0
- package/src/components/resources/resources-column-filter.test.ts +74 -0
- package/src/components/shared/ResourceActionsBar.tsx +77 -0
- package/src/components/shared/ResourceRendererDispatch.tsx +138 -9
- package/src/components/ui/YamlEditor.tsx +28 -15
- package/src/components/ui/drawer-components.tsx +8 -20
- package/src/components/workload/ResourceDetailDrawer.tsx +1 -1
- package/src/components/workload/WorkloadView.tsx +2 -2
- package/src/index.ts +3 -0
- package/src/types/core.ts +31 -6
- package/src/types/index.ts +1 -0
- package/src/types/rbac.ts +99 -0
- package/src/utils/api-resources.ts +9 -1
- package/src/utils/badge-colors.ts +25 -0
- package/src/utils/gitops-owner.test.ts +70 -111
- package/src/utils/gitops-owner.ts +37 -74
- package/src/utils/helm-status.test.ts +50 -0
- package/src/utils/index.ts +1 -0
- package/src/utils/rbac-badges.ts +61 -0
- package/src/utils/rbac-blast-radius.test.ts +137 -0
- package/src/utils/rbac-blast-radius.ts +98 -0
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
// Pure helpers for the "what's the blast radius of this Pod / Workload / SA"
|
|
2
|
+
// detection. Lives outside the renderers because it's the security-UX core
|
|
3
|
+
// of the RBAC visibility feature — the rule that resource-only wildcards do
|
|
4
|
+
// NOT trigger (because `view` covers every fluxcd CRD via wildcards and would
|
|
5
|
+
// alarm on every authenticated SA) is load-bearing and easily regressed
|
|
6
|
+
// silently. Tested via rbac-blast-radius.test.ts; keep it pure.
|
|
7
|
+
|
|
8
|
+
import type { RBACBindingRules, RBACPolicyRule, RBACSubjectResponse } from '../types/rbac'
|
|
9
|
+
|
|
10
|
+
export const RBAC_BLAST_READ_VERBS = new Set(['get', 'list', 'watch'])
|
|
11
|
+
export const RBAC_BLAST_WRITE_VERBS = new Set(['create', 'update', 'patch'])
|
|
12
|
+
export const RBAC_BLAST_DELETE_VERBS = new Set(['delete', 'deletecollection'])
|
|
13
|
+
export const RBAC_BLAST_ESCALATION_VERBS = new Set(['escalate', 'bind', 'impersonate'])
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Score a single PolicyRule by max permissiveness. Higher is scarier.
|
|
17
|
+
* The ordering matters more than the absolute scale — used to sort the
|
|
18
|
+
* rule preview so dangerous rules surface first in incident response.
|
|
19
|
+
*/
|
|
20
|
+
export function rulePermissivenessScore(r: RBACPolicyRule): number {
|
|
21
|
+
let score = 0
|
|
22
|
+
const verbs = r.verbs ?? []
|
|
23
|
+
const resources = r.resources ?? []
|
|
24
|
+
const groups = r.apiGroups ?? []
|
|
25
|
+
if (verbs.includes('*')) score += 100
|
|
26
|
+
if (resources.includes('*')) score += 100
|
|
27
|
+
if (groups.includes('*')) score += 100
|
|
28
|
+
for (const v of verbs) {
|
|
29
|
+
if (RBAC_BLAST_ESCALATION_VERBS.has(v)) score += 80
|
|
30
|
+
else if (RBAC_BLAST_DELETE_VERBS.has(v)) score += 40
|
|
31
|
+
else if (RBAC_BLAST_WRITE_VERBS.has(v)) score += 20
|
|
32
|
+
else if (RBAC_BLAST_READ_VERBS.has(v)) score += 5
|
|
33
|
+
else score += 10
|
|
34
|
+
}
|
|
35
|
+
if (verbs.includes('create') && resources.includes('pods')) score += 50
|
|
36
|
+
return score
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface BlastRadiusReason {
|
|
40
|
+
binding: RBACBindingRules
|
|
41
|
+
reason: string
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Detect bindings that genuinely make a Pod's compromise a cluster takeover.
|
|
46
|
+
* Triggers on, in priority order:
|
|
47
|
+
*
|
|
48
|
+
* 1. cluster-admin role-name match (covers the "bound my SA to cluster-admin
|
|
49
|
+
* for testing" pattern even when rules aren't loaded)
|
|
50
|
+
* 2. verb wildcards (`*`)
|
|
51
|
+
* 3. escalation verbs (`escalate` / `bind` / `impersonate`)
|
|
52
|
+
* 4. cluster-wide `create pods` (lateral-movement vector)
|
|
53
|
+
*
|
|
54
|
+
* Resource-only wildcards (e.g. `view`'s fluxcd CRD coverage) deliberately
|
|
55
|
+
* do NOT trigger — they fire on every authenticated SA and would train
|
|
56
|
+
* operators to ignore the alarm. The current set is the calibrated minimum
|
|
57
|
+
* for "the Pod's identity is genuinely god-mode".
|
|
58
|
+
*
|
|
59
|
+
* Dedupes by binding identity so a binding with multiple risky rules
|
|
60
|
+
* counts once; first matching reason wins.
|
|
61
|
+
*/
|
|
62
|
+
export function detectBlastRadius(rbacData: RBACSubjectResponse): BlastRadiusReason[] {
|
|
63
|
+
const reasons: BlastRadiusReason[] = []
|
|
64
|
+
const allBindings = [
|
|
65
|
+
...rbacData.direct,
|
|
66
|
+
...rbacData.inheritedFromGroups.flatMap((g) => g.bindings),
|
|
67
|
+
]
|
|
68
|
+
for (const br of allBindings) {
|
|
69
|
+
if (br.role.name === 'cluster-admin' && br.role.kind === 'ClusterRole') {
|
|
70
|
+
reasons.push({ binding: br, reason: 'bound to cluster-admin (full god-mode access)' })
|
|
71
|
+
continue
|
|
72
|
+
}
|
|
73
|
+
for (const r of br.rules ?? []) {
|
|
74
|
+
const verbs = r.verbs ?? []
|
|
75
|
+
const resources = r.resources ?? []
|
|
76
|
+
if (verbs.includes('*')) {
|
|
77
|
+
reasons.push({ binding: br, reason: 'grants verb wildcard (*) — every action on the listed resources' })
|
|
78
|
+
break
|
|
79
|
+
}
|
|
80
|
+
const escalations = verbs.filter((v) => RBAC_BLAST_ESCALATION_VERBS.has(v))
|
|
81
|
+
if (escalations.length > 0) {
|
|
82
|
+
reasons.push({ binding: br, reason: `grants privilege-escalation verbs: ${escalations.join(', ')}` })
|
|
83
|
+
break
|
|
84
|
+
}
|
|
85
|
+
if (verbs.includes('create') && resources.includes('pods') && br.binding.kind === 'ClusterRoleBinding') {
|
|
86
|
+
reasons.push({ binding: br, reason: 'grants cluster-wide "create pods" — a known lateral-movement vector' })
|
|
87
|
+
break
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
const seen = new Set<string>()
|
|
92
|
+
return reasons.filter((r) => {
|
|
93
|
+
const key = r.binding.binding.kind + '/' + r.binding.binding.namespace + '/' + r.binding.binding.name
|
|
94
|
+
if (seen.has(key)) return false
|
|
95
|
+
seen.add(key)
|
|
96
|
+
return true
|
|
97
|
+
})
|
|
98
|
+
}
|