commitguardian 0.1.0__py3-none-any.whl
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.
- commitguard/__init__.py +26 -0
- commitguard/__main__.py +6 -0
- commitguard/api/__init__.py +18 -0
- commitguard/api/app.py +1376 -0
- commitguard/api/governance.py +1085 -0
- commitguard/api/hosting.py +196 -0
- commitguard/api/http.py +252 -0
- commitguard/api/settings.py +169 -0
- commitguard/audit/__init__.py +13 -0
- commitguard/audit/logger.py +34 -0
- commitguard/audit/models.py +222 -0
- commitguard/audit/storage.py +59 -0
- commitguard/ci/__init__.py +7 -0
- commitguard/ci/context.py +60 -0
- commitguard/cli/__init__.py +6 -0
- commitguard/cli/app.py +74 -0
- commitguard/cli/commands/__init__.py +1 -0
- commitguard/cli/commands/benchmark.py +441 -0
- commitguard/cli/commands/check.py +100 -0
- commitguard/cli/commands/ci.py +165 -0
- commitguard/cli/commands/dashboard.py +141 -0
- commitguard/cli/commands/doctor.py +533 -0
- commitguard/cli/commands/github.py +449 -0
- commitguard/cli/commands/hook.py +156 -0
- commitguard/cli/commands/init.py +137 -0
- commitguard/cli/commands/install.py +152 -0
- commitguard/cli/commands/policy.py +36 -0
- commitguard/cli/commands/report.py +39 -0
- commitguard/cli/commands/reproduce.py +123 -0
- commitguard/cli/commands/scan.py +47 -0
- commitguard/cli/common.py +44 -0
- commitguard/cli/output.py +89 -0
- commitguard/cli/render.py +367 -0
- commitguard/config/__init__.py +6 -0
- commitguard/config/defaults.py +53 -0
- commitguard/config/enforcement.py +53 -0
- commitguard/config/loader.py +174 -0
- commitguard/config/schema.py +105 -0
- commitguard/config/sources.py +183 -0
- commitguard/controlplane/__init__.py +24 -0
- commitguard/controlplane/access.py +231 -0
- commitguard/controlplane/commands.py +393 -0
- commitguard/controlplane/errors.py +88 -0
- commitguard/controlplane/identity.py +478 -0
- commitguard/controlplane/members.py +219 -0
- commitguard/controlplane/notifications.py +787 -0
- commitguard/controlplane/pagination.py +146 -0
- commitguard/controlplane/policies.py +1204 -0
- commitguard/controlplane/queries.py +1814 -0
- commitguard/controlplane/results.py +909 -0
- commitguard/controlplane/rules.py +184 -0
- commitguard/controlplane/views.py +799 -0
- commitguard/core/__init__.py +6 -0
- commitguard/core/context.py +31 -0
- commitguard/core/decision.py +58 -0
- commitguard/core/engine.py +82 -0
- commitguard/core/result.py +177 -0
- commitguard/detectors/__init__.py +6 -0
- commitguard/detectors/base.py +58 -0
- commitguard/detectors/bot.py +87 -0
- commitguard/detectors/coauthor.py +86 -0
- commitguard/detectors/identity.py +76 -0
- commitguard/detectors/registry.py +72 -0
- commitguard/detectors/trailer.py +211 -0
- commitguard/exceptions/__init__.py +33 -0
- commitguard/exceptions/base.py +9 -0
- commitguard/exceptions/configuration.py +22 -0
- commitguard/exceptions/detection.py +11 -0
- commitguard/exceptions/git.py +41 -0
- commitguard/exceptions/service.py +25 -0
- commitguard/git/__init__.py +12 -0
- commitguard/git/commands.py +101 -0
- commitguard/git/commit.py +97 -0
- commitguard/git/diff.py +36 -0
- commitguard/git/hooks.py +527 -0
- commitguard/git/push.py +93 -0
- commitguard/git/ranges.py +71 -0
- commitguard/git/repository.py +447 -0
- commitguard/github/__init__.py +34 -0
- commitguard/github/actions.py +163 -0
- commitguard/github/app.py +935 -0
- commitguard/github/auth.py +217 -0
- commitguard/github/check_runs.py +172 -0
- commitguard/github/checks.py +210 -0
- commitguard/github/client.py +844 -0
- commitguard/github/enforcement_status.py +209 -0
- commitguard/github/errors.py +129 -0
- commitguard/github/events.py +563 -0
- commitguard/github/identifiers.py +90 -0
- commitguard/github/installations.py +566 -0
- commitguard/github/markdown.py +19 -0
- commitguard/github/permissions.py +70 -0
- commitguard/github/pull_requests.py +53 -0
- commitguard/github/queue.py +47 -0
- commitguard/github/recovery.py +124 -0
- commitguard/github/repositories.py +305 -0
- commitguard/github/server.py +52 -0
- commitguard/github/settings.py +174 -0
- commitguard/github/storage.py +2315 -0
- commitguard/github/webhooks.py +129 -0
- commitguard/github/worker.py +628 -0
- commitguard/github/workflow.py +286 -0
- commitguard/governance/__init__.py +26 -0
- commitguard/governance/bulk.py +765 -0
- commitguard/governance/cache.py +88 -0
- commitguard/governance/common.py +216 -0
- commitguard/governance/exceptions.py +861 -0
- commitguard/governance/groups.py +448 -0
- commitguard/governance/inventory.py +386 -0
- commitguard/governance/posture.py +1272 -0
- commitguard/governance/resolver.py +632 -0
- commitguard/governance/rollouts.py +760 -0
- commitguard/governance/rules.py +371 -0
- commitguard/governance/schedules.py +663 -0
- commitguard/governance/service.py +120 -0
- commitguard/governance/settings.py +365 -0
- commitguard/governance/simulation.py +618 -0
- commitguard/governance/workflow.py +734 -0
- commitguard/notifications/__init__.py +2 -0
- commitguard/notifications/channels/__init__.py +1 -0
- commitguard/notifications/channels/base.py +22 -0
- commitguard/notifications/channels/email.py +110 -0
- commitguard/notifications/channels/in_app.py +74 -0
- commitguard/notifications/channels/sink.py +58 -0
- commitguard/notifications/channels/webhook.py +233 -0
- commitguard/notifications/deduplication.py +57 -0
- commitguard/notifications/dispatcher.py +201 -0
- commitguard/notifications/models.py +439 -0
- commitguard/notifications/outbox.py +106 -0
- commitguard/notifications/preferences.py +224 -0
- commitguard/notifications/retry.py +282 -0
- commitguard/notifications/service.py +128 -0
- commitguard/notifications/settings.py +167 -0
- commitguard/notifications/templates.py +108 -0
- commitguard/observability/__init__.py +5 -0
- commitguard/observability/logging.py +161 -0
- commitguard/observability/metrics.py +105 -0
- commitguard/policies/__init__.py +6 -0
- commitguard/policies/defaults.py +48 -0
- commitguard/policies/evaluator.py +66 -0
- commitguard/policies/governance.py +498 -0
- commitguard/policies/loader.py +23 -0
- commitguard/policies/mandatory.py +52 -0
- commitguard/policies/model.py +46 -0
- commitguard/provenance/__init__.py +9 -0
- commitguard/provenance/author.py +146 -0
- commitguard/provenance/committer.py +16 -0
- commitguard/provenance/normalization.py +158 -0
- commitguard/provenance/signatures.py +34 -0
- commitguard/provenance/trailers.py +256 -0
- commitguard/research/__init__.py +26 -0
- commitguard/research/compare.py +231 -0
- commitguard/research/datasets.py +1484 -0
- commitguard/research/detection.py +183 -0
- commitguard/research/environment.py +185 -0
- commitguard/research/gitenv.py +108 -0
- commitguard/research/hooks.py +247 -0
- commitguard/research/metrics.py +85 -0
- commitguard/research/performance.py +194 -0
- commitguard/research/platform.py +288 -0
- commitguard/research/report.py +372 -0
- commitguard/research/repository.py +111 -0
- commitguard/research/reproduction.py +297 -0
- commitguard/research/results.py +94 -0
- commitguard/rules/__init__.py +11 -0
- commitguard/rules/data/ai-domains.yaml +51 -0
- commitguard/rules/data/ai-identities.yaml +131 -0
- commitguard/rules/data/bot-identities.yaml +53 -0
- commitguard/rules/data/patterns.yaml +52 -0
- commitguard/rules/loader.py +102 -0
- commitguard/rules/matcher.py +212 -0
- commitguard/rules/models.py +269 -0
- commitguard/security/__init__.py +5 -0
- commitguard/security/hashing.py +30 -0
- commitguard/security/rate_limit.py +33 -0
- commitguard/security/safe_yaml.py +69 -0
- commitguard/security/sanitization.py +85 -0
- commitguard/security/secrets.py +169 -0
- commitguard/security/validation.py +89 -0
- commitguard/services/__init__.py +15 -0
- commitguard/services/analysis.py +119 -0
- commitguard/services/audit.py +95 -0
- commitguard/services/ci.py +383 -0
- commitguard/services/enforcement.py +102 -0
- commitguard/services/hooks.py +254 -0
- commitguard/services/remediation.py +99 -0
- commitguard/services/reports.py +146 -0
- commitguard/services/scan.py +172 -0
- commitguard/utils/__init__.py +1 -0
- commitguard/utils/filesystem.py +72 -0
- commitguard/utils/platform.py +35 -0
- commitguard/utils/subprocess.py +84 -0
- commitguardian-0.1.0.dist-info/METADATA +694 -0
- commitguardian-0.1.0.dist-info/RECORD +197 -0
- commitguardian-0.1.0.dist-info/WHEEL +4 -0
- commitguardian-0.1.0.dist-info/entry_points.txt +2 -0
- commitguardian-0.1.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
"""The rule catalogue shown in the dashboard.
|
|
2
|
+
|
|
3
|
+
Rule IDs are the IDs the detectors emit and policies configure - the same
|
|
4
|
+
``ai_coauthor`` the CLI, hooks, GitHub Action and GitHub App print. This module
|
|
5
|
+
only *describes* them for display (name, detector, severity, remediation
|
|
6
|
+
steps); detection stays in :mod:`commitguard.detectors` and decisions in
|
|
7
|
+
:mod:`commitguard.policies`. A unit test checks that the catalogue matches the
|
|
8
|
+
registered detectors, the built-in policies and the severities the detectors
|
|
9
|
+
actually emit, so the description cannot drift from the behaviour.
|
|
10
|
+
|
|
11
|
+
Rules are bundled with the installed package and are trusted; there are no
|
|
12
|
+
repository-defined rules, so nothing here is editable from the dashboard.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from dataclasses import dataclass
|
|
16
|
+
from functools import cache
|
|
17
|
+
|
|
18
|
+
from commitguard import __version__
|
|
19
|
+
from commitguard.controlplane.views import RuleDataFile, RuleDetail, RuleView
|
|
20
|
+
from commitguard.core.result import EvidenceSource, Severity
|
|
21
|
+
from commitguard.policies.defaults import DEFAULT_POLICIES
|
|
22
|
+
from commitguard.rules.loader import builtin_rules_fingerprint, load_builtin_rules
|
|
23
|
+
from commitguard.rules.models import (
|
|
24
|
+
AI_DOMAINS_FILE,
|
|
25
|
+
AI_IDENTITIES_FILE,
|
|
26
|
+
BOT_IDENTITIES_FILE,
|
|
27
|
+
PATTERNS_FILE,
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
RULES_VERSION_LENGTH = 12
|
|
31
|
+
NO_REWRITE_NOTICE = "CommitGuard does not rewrite Git history or modify commits automatically."
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@dataclass(frozen=True, slots=True)
|
|
35
|
+
class RuleDescription:
|
|
36
|
+
rule_id: str
|
|
37
|
+
name: str
|
|
38
|
+
detector: str
|
|
39
|
+
severity: Severity
|
|
40
|
+
evidence_sources: tuple[EvidenceSource, ...]
|
|
41
|
+
data_files: tuple[str, ...]
|
|
42
|
+
steps: tuple[str, ...]
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
_HISTORY_STEPS = (
|
|
46
|
+
"Rewrite the affected commit on your branch (for example `git commit --amend` for the "
|
|
47
|
+
"latest commit, or an interactive rebase for older ones) and push the corrected branch.",
|
|
48
|
+
"CommitGuard scans the updated pull request or branch automatically; the violation is "
|
|
49
|
+
"resolved when the commit is no longer part of it.",
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
CATALOG: tuple[RuleDescription, ...] = (
|
|
53
|
+
RuleDescription(
|
|
54
|
+
rule_id="ai_coauthor",
|
|
55
|
+
name="AI co-author attribution",
|
|
56
|
+
detector="coauthor",
|
|
57
|
+
severity=Severity.HIGH,
|
|
58
|
+
evidence_sources=(EvidenceSource.COAUTHOR_TRAILER,),
|
|
59
|
+
data_files=(PATTERNS_FILE, AI_IDENTITIES_FILE, AI_DOMAINS_FILE),
|
|
60
|
+
steps=(
|
|
61
|
+
"Remove the Co-authored-by line that names the AI agent from the commit message.",
|
|
62
|
+
*_HISTORY_STEPS,
|
|
63
|
+
),
|
|
64
|
+
),
|
|
65
|
+
RuleDescription(
|
|
66
|
+
rule_id="ai_identity",
|
|
67
|
+
name="AI agent as author or committer",
|
|
68
|
+
detector="identity",
|
|
69
|
+
severity=Severity.HIGH,
|
|
70
|
+
evidence_sources=(EvidenceSource.AUTHOR, EvidenceSource.COMMITTER),
|
|
71
|
+
data_files=(AI_IDENTITIES_FILE, AI_DOMAINS_FILE),
|
|
72
|
+
steps=(
|
|
73
|
+
"Recreate the commit under the responsible human contributor's identity "
|
|
74
|
+
"(for example `git commit --amend --reset-author`).",
|
|
75
|
+
*_HISTORY_STEPS,
|
|
76
|
+
),
|
|
77
|
+
),
|
|
78
|
+
RuleDescription(
|
|
79
|
+
rule_id="ai_trailer",
|
|
80
|
+
name="AI attribution trailer or footer",
|
|
81
|
+
detector="trailer",
|
|
82
|
+
severity=Severity.HIGH,
|
|
83
|
+
evidence_sources=(EvidenceSource.TRAILER, EvidenceSource.MESSAGE),
|
|
84
|
+
data_files=(PATTERNS_FILE,),
|
|
85
|
+
steps=(
|
|
86
|
+
"Remove the attribution trailer or tool-generated footer from the commit message.",
|
|
87
|
+
*_HISTORY_STEPS,
|
|
88
|
+
),
|
|
89
|
+
),
|
|
90
|
+
RuleDescription(
|
|
91
|
+
rule_id="malformed_trailer",
|
|
92
|
+
name="Malformed trailer",
|
|
93
|
+
detector="trailer",
|
|
94
|
+
severity=Severity.LOW,
|
|
95
|
+
evidence_sources=(EvidenceSource.TRAILER,),
|
|
96
|
+
data_files=(PATTERNS_FILE,),
|
|
97
|
+
steps=(
|
|
98
|
+
"Rewrite the trailer as `Key: Name <email>` or remove it; malformed trailers can be "
|
|
99
|
+
"used to hide attribution from parsers.",
|
|
100
|
+
*_HISTORY_STEPS,
|
|
101
|
+
),
|
|
102
|
+
),
|
|
103
|
+
RuleDescription(
|
|
104
|
+
rule_id="bot_identity",
|
|
105
|
+
name="Automation or bot identity",
|
|
106
|
+
detector="bot",
|
|
107
|
+
severity=Severity.LOW,
|
|
108
|
+
evidence_sources=(
|
|
109
|
+
EvidenceSource.AUTHOR,
|
|
110
|
+
EvidenceSource.COMMITTER,
|
|
111
|
+
EvidenceSource.COAUTHOR_TRAILER,
|
|
112
|
+
),
|
|
113
|
+
data_files=(BOT_IDENTITIES_FILE,),
|
|
114
|
+
steps=(
|
|
115
|
+
"Confirm the automation is expected in this repository.",
|
|
116
|
+
"If it is, an administrator can lower the policy for bot_identity in the repository's "
|
|
117
|
+
"trusted .commitguard.yaml; otherwise investigate where the commit came from.",
|
|
118
|
+
),
|
|
119
|
+
),
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
CATALOG_BY_ID = {entry.rule_id: entry for entry in CATALOG}
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def rules_version() -> str:
|
|
126
|
+
return builtin_rules_fingerprint()[:RULES_VERSION_LENGTH]
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def remediation_steps(rule_id: str) -> tuple[str, ...]:
|
|
130
|
+
entry = CATALOG_BY_ID.get(rule_id)
|
|
131
|
+
steps = entry.steps if entry else ()
|
|
132
|
+
return (*steps, NO_REWRITE_NOTICE)
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def rule_view(entry: RuleDescription) -> RuleView:
|
|
136
|
+
policy = DEFAULT_POLICIES[entry.rule_id]
|
|
137
|
+
return RuleView(
|
|
138
|
+
id=entry.rule_id,
|
|
139
|
+
name=entry.name,
|
|
140
|
+
description=policy.description,
|
|
141
|
+
detector=entry.detector,
|
|
142
|
+
severity=entry.severity,
|
|
143
|
+
default_action=policy.action,
|
|
144
|
+
source="bundled",
|
|
145
|
+
trusted=True,
|
|
146
|
+
status="active",
|
|
147
|
+
rules_version=rules_version(),
|
|
148
|
+
tool_version=__version__,
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def list_rules() -> tuple[RuleView, ...]:
|
|
153
|
+
return tuple(rule_view(entry) for entry in CATALOG)
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
@cache
|
|
157
|
+
def _data_file_entries() -> dict[str, int]:
|
|
158
|
+
rules = load_builtin_rules().rules
|
|
159
|
+
patterns = rules.patterns
|
|
160
|
+
return {
|
|
161
|
+
AI_IDENTITIES_FILE: len(rules.ai_identities.agents),
|
|
162
|
+
AI_DOMAINS_FILE: len(rules.ai_domains.domains),
|
|
163
|
+
BOT_IDENTITIES_FILE: len(rules.bots.bots),
|
|
164
|
+
PATTERNS_FILE: len(patterns.coauthor_trailer_keys)
|
|
165
|
+
+ len(patterns.attribution_trailers)
|
|
166
|
+
+ len(patterns.malformed_trailer_checks)
|
|
167
|
+
+ len(patterns.message_markers),
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def rule_detail(rule_id: str) -> RuleDetail | None:
|
|
172
|
+
entry = CATALOG_BY_ID.get(rule_id)
|
|
173
|
+
if entry is None:
|
|
174
|
+
return None
|
|
175
|
+
counts = _data_file_entries()
|
|
176
|
+
return RuleDetail(
|
|
177
|
+
rule=rule_view(entry),
|
|
178
|
+
remediation=remediation_steps(rule_id),
|
|
179
|
+
evidence_sources=tuple(source.label for source in entry.evidence_sources),
|
|
180
|
+
data_files=tuple(
|
|
181
|
+
RuleDataFile(name=name, entries=counts[name]) for name in entry.data_files
|
|
182
|
+
),
|
|
183
|
+
editable=False,
|
|
184
|
+
)
|