cycode 3.12.3.dev3__py3-none-any.whl → 3.12.3.dev5__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.
- cycode/__init__.py +1 -1
- cycode/cli/apps/ai_guardrails/scan/claude_config.py +44 -0
- cycode/cli/apps/ai_guardrails/scan/payload.py +6 -1
- cycode/cli/apps/scan/pre_push/pre_push_command.py +4 -0
- cycode/cli/files_collector/commit_range_documents.py +12 -9
- {cycode-3.12.3.dev3.dist-info → cycode-3.12.3.dev5.dist-info}/METADATA +1 -1
- {cycode-3.12.3.dev3.dist-info → cycode-3.12.3.dev5.dist-info}/RECORD +10 -9
- {cycode-3.12.3.dev3.dist-info → cycode-3.12.3.dev5.dist-info}/WHEEL +0 -0
- {cycode-3.12.3.dev3.dist-info → cycode-3.12.3.dev5.dist-info}/entry_points.txt +0 -0
- {cycode-3.12.3.dev3.dist-info → cycode-3.12.3.dev5.dist-info}/licenses/LICENCE +0 -0
cycode/__init__.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = '3.12.3.
|
|
1
|
+
__version__ = '3.12.3.dev5' # DON'T TOUCH. Placeholder. Will be filled automatically on poetry build from Git Tag
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"""Reader for ~/.claude.json configuration file.
|
|
2
|
+
|
|
3
|
+
Extracts user email from the Claude Code global config file
|
|
4
|
+
for use in AI guardrails scan enrichment.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import json
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import Optional
|
|
10
|
+
|
|
11
|
+
from cycode.logger import get_logger
|
|
12
|
+
|
|
13
|
+
logger = get_logger('AI Guardrails Claude Config')
|
|
14
|
+
|
|
15
|
+
_CLAUDE_CONFIG_PATH = Path.home() / '.claude.json'
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def load_claude_config(config_path: Optional[Path] = None) -> Optional[dict]:
|
|
19
|
+
"""Load and parse ~/.claude.json.
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
config_path: Override path for testing. Defaults to ~/.claude.json.
|
|
23
|
+
|
|
24
|
+
Returns:
|
|
25
|
+
Parsed dict or None if file is missing or invalid.
|
|
26
|
+
"""
|
|
27
|
+
path = config_path or _CLAUDE_CONFIG_PATH
|
|
28
|
+
if not path.exists():
|
|
29
|
+
logger.debug('Claude config file not found', extra={'path': str(path)})
|
|
30
|
+
return None
|
|
31
|
+
try:
|
|
32
|
+
content = path.read_text(encoding='utf-8')
|
|
33
|
+
return json.loads(content)
|
|
34
|
+
except Exception as e:
|
|
35
|
+
logger.debug('Failed to load Claude config file', exc_info=e)
|
|
36
|
+
return None
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def get_user_email(config: dict) -> Optional[str]:
|
|
40
|
+
"""Extract user email from Claude config.
|
|
41
|
+
|
|
42
|
+
Reads oauthAccount.emailAddress from the config dict.
|
|
43
|
+
"""
|
|
44
|
+
return config.get('oauthAccount', {}).get('emailAddress')
|
|
@@ -7,6 +7,7 @@ from pathlib import Path
|
|
|
7
7
|
from typing import Optional
|
|
8
8
|
|
|
9
9
|
from cycode.cli.apps.ai_guardrails.consts import AIIDEType
|
|
10
|
+
from cycode.cli.apps.ai_guardrails.scan.claude_config import get_user_email, load_claude_config
|
|
10
11
|
from cycode.cli.apps.ai_guardrails.scan.types import (
|
|
11
12
|
CLAUDE_CODE_EVENT_MAPPING,
|
|
12
13
|
CLAUDE_CODE_EVENT_NAMES,
|
|
@@ -207,11 +208,15 @@ class AIHookPayload:
|
|
|
207
208
|
# Extract IDE version, model, and generation ID from transcript file
|
|
208
209
|
ide_version, model, generation_id = _extract_from_claude_transcript(payload.get('transcript_path'))
|
|
209
210
|
|
|
211
|
+
# Extract user email from ~/.claude.json
|
|
212
|
+
claude_config = load_claude_config()
|
|
213
|
+
ide_user_email = get_user_email(claude_config) if claude_config else None
|
|
214
|
+
|
|
210
215
|
return cls(
|
|
211
216
|
event_name=canonical_event,
|
|
212
217
|
conversation_id=payload.get('session_id'),
|
|
213
218
|
generation_id=generation_id,
|
|
214
|
-
ide_user_email=
|
|
219
|
+
ide_user_email=ide_user_email,
|
|
215
220
|
model=model,
|
|
216
221
|
ide_provider=AIIDEType.CLAUDE_CODE.value,
|
|
217
222
|
ide_version=ide_version,
|
|
@@ -44,6 +44,10 @@ def pre_push_command(
|
|
|
44
44
|
timeout = configuration_manager.get_pre_push_command_timeout(command_scan_type)
|
|
45
45
|
with TimeoutAfter(timeout):
|
|
46
46
|
push_update_details = parse_pre_push_input()
|
|
47
|
+
if not push_update_details:
|
|
48
|
+
logger.info('No pre-push input found, nothing to scan')
|
|
49
|
+
return
|
|
50
|
+
|
|
47
51
|
commit_range = calculate_pre_push_commit_range(push_update_details)
|
|
48
52
|
if not commit_range:
|
|
49
53
|
logger.info(
|
|
@@ -228,7 +228,7 @@ def parse_pre_receive_input() -> str:
|
|
|
228
228
|
return pre_receive_input.splitlines()[0]
|
|
229
229
|
|
|
230
230
|
|
|
231
|
-
def parse_pre_push_input() -> str:
|
|
231
|
+
def parse_pre_push_input() -> Optional[str]:
|
|
232
232
|
"""Parse input to pre-push hook details.
|
|
233
233
|
|
|
234
234
|
Example input:
|
|
@@ -237,13 +237,11 @@ def parse_pre_push_input() -> str:
|
|
|
237
237
|
refs/heads/main 9cf90954ef26e7c58284f8ebf7dcd0fcf711152a refs/heads/main 973a96d3e925b65941f7c47fa16129f1577d499f
|
|
238
238
|
refs/heads/feature-branch 3378e52dcfa47fb11ce3a4a520bea5f85d5d0bf3 refs/heads/feature-branch 59564ef68745bca38c42fc57a7822efd519a6bd9
|
|
239
239
|
|
|
240
|
-
:return: First
|
|
240
|
+
:return: First push update details (input's first line), or None if no input was provided
|
|
241
241
|
""" # noqa: E501
|
|
242
242
|
pre_push_input = _read_hook_input_from_stdin()
|
|
243
243
|
if not pre_push_input:
|
|
244
|
-
|
|
245
|
-
'Pre push input was not found. Make sure that you are using this command only in pre-push hook'
|
|
246
|
-
)
|
|
244
|
+
return None
|
|
247
245
|
|
|
248
246
|
# each line represents a branch push request, handle the first one only
|
|
249
247
|
return pre_push_input.splitlines()[0]
|
|
@@ -332,6 +330,15 @@ def calculate_pre_push_commit_range(push_update_details: str) -> Optional[str]:
|
|
|
332
330
|
"""
|
|
333
331
|
local_ref, local_object_name, remote_ref, remote_object_name = push_update_details.split()
|
|
334
332
|
|
|
333
|
+
# Tag pushes don't contain file diffs that need scanning
|
|
334
|
+
if local_ref.startswith('refs/tags/') or remote_ref.startswith('refs/tags/'):
|
|
335
|
+
logger.info('Skipping scan for tag push: %s -> %s', local_ref, remote_ref)
|
|
336
|
+
return None
|
|
337
|
+
|
|
338
|
+
# If deleting a ref (local_object_name is all zeros), no need to scan
|
|
339
|
+
if local_object_name == consts.EMPTY_COMMIT_SHA:
|
|
340
|
+
return None
|
|
341
|
+
|
|
335
342
|
if remote_object_name == consts.EMPTY_COMMIT_SHA:
|
|
336
343
|
try:
|
|
337
344
|
repo = git_proxy.get_repo(os.getcwd())
|
|
@@ -356,10 +363,6 @@ def calculate_pre_push_commit_range(push_update_details: str) -> Optional[str]:
|
|
|
356
363
|
logger.debug('Failed to get repo for pre-push commit range calculation: %s', exc_info=e)
|
|
357
364
|
return consts.COMMIT_RANGE_ALL_COMMITS
|
|
358
365
|
|
|
359
|
-
# If deleting a branch (local_object_name is all zeros), no need to scan
|
|
360
|
-
if local_object_name == consts.EMPTY_COMMIT_SHA:
|
|
361
|
-
return None
|
|
362
|
-
|
|
363
366
|
# For updates to existing branches, scan from remote to local
|
|
364
367
|
return f'{remote_object_name}..{local_object_name}'
|
|
365
368
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
cycode/__init__.py,sha256=
|
|
1
|
+
cycode/__init__.py,sha256=FYtGGWFeHBebJ4ZXbcB6omMDRUbH3lV8_EjHiWXUNbQ,115
|
|
2
2
|
cycode/__main__.py,sha256=Z3bD5yrA7yPvAChcADQrqCaZd0ChGI1gdiwALwbWJ6U,104
|
|
3
3
|
cycode/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
4
|
cycode/cli/app.py,sha256=bsfXV85RRb1iz19JRC9gkc5Iv30fnEE1cwA8dg552NQ,6482
|
|
@@ -11,9 +11,10 @@ cycode/cli/apps/ai_guardrails/ensure_auth_command.py,sha256=SwgafehweFCPt7Qtbprs
|
|
|
11
11
|
cycode/cli/apps/ai_guardrails/hooks_manager.py,sha256=37IcEMCK60pQ8lnuy8GThlq9oeNOfETVp_xYGeJ9EpM,9428
|
|
12
12
|
cycode/cli/apps/ai_guardrails/install_command.py,sha256=qlklts1Uj6j3urK6jwAWJY-L_DgVaZWuk7vZcpoKPAQ,4571
|
|
13
13
|
cycode/cli/apps/ai_guardrails/scan/__init__.py,sha256=qJc82XiQGiAuc1sYY8Ij_A-qXpxgLPuayQq8xWlouMA,48
|
|
14
|
+
cycode/cli/apps/ai_guardrails/scan/claude_config.py,sha256=JCPpSHUroH6tF2URoXCsf_AszqrSsSDcpM8ST4uJqHQ,1262
|
|
14
15
|
cycode/cli/apps/ai_guardrails/scan/consts.py,sha256=drAslw6vW3kxmbCs2qPCUbUPR7PJouT2lsXtu5sD-lQ,1094
|
|
15
16
|
cycode/cli/apps/ai_guardrails/scan/handlers.py,sha256=e3UlQ6TbFFFAFEAgMUhL5i7bV1mSrmFJuLb9O0FLX2Y,15702
|
|
16
|
-
cycode/cli/apps/ai_guardrails/scan/payload.py,sha256
|
|
17
|
+
cycode/cli/apps/ai_guardrails/scan/payload.py,sha256=wUk9PEtVrym0SaChiML8_7KGa0NmpRNIrcojKA3msjU,10409
|
|
17
18
|
cycode/cli/apps/ai_guardrails/scan/policy.py,sha256=39s8hnxgjny1l6XAO59wsRcAlpW-LG00GUnO0PfqvuY,2566
|
|
18
19
|
cycode/cli/apps/ai_guardrails/scan/response_builders.py,sha256=tVFJCnGdqSmyileg-idypOihygct7F6T4KHXYlX8y_c,4653
|
|
19
20
|
cycode/cli/apps/ai_guardrails/scan/scan_command.py,sha256=_2fa6vz8ZmJlvCYrYNoWfX9fWrrpzcNCwL1UD-JxqLM,5618
|
|
@@ -66,7 +67,7 @@ cycode/cli/apps/scan/path/path_command.py,sha256=x4HXqq1Wy6onziKMc6ELQxqeI5k-m3t
|
|
|
66
67
|
cycode/cli/apps/scan/pre_commit/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
67
68
|
cycode/cli/apps/scan/pre_commit/pre_commit_command.py,sha256=49vnVAIiLtcG7lsBX2vKhS-5kqC8wKqCUP-3G8ClPVs,465
|
|
68
69
|
cycode/cli/apps/scan/pre_push/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
69
|
-
cycode/cli/apps/scan/pre_push/pre_push_command.py,sha256=
|
|
70
|
+
cycode/cli/apps/scan/pre_push/pre_push_command.py,sha256=i2k1-il7tOtuIj2ZksjdtgnRVXbj0p6tMrMQuxckynY,2554
|
|
70
71
|
cycode/cli/apps/scan/pre_receive/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
71
72
|
cycode/cli/apps/scan/pre_receive/pre_receive_command.py,sha256=v1sXjl9EKTG65065DzJRJ78we96cqLztzDWw89KUb0o,2522
|
|
72
73
|
cycode/cli/apps/scan/remote_url_resolver.py,sha256=JCjaAzDMxGFDT7twBpZzjgEHGwlaGIxWG_Y7DSehsb0,5651
|
|
@@ -95,7 +96,7 @@ cycode/cli/exceptions/handle_errors.py,sha256=za3vQcM_eFTvbT-53tTc6ky-J0wav6lupD
|
|
|
95
96
|
cycode/cli/exceptions/handle_report_sbom_errors.py,sha256=bi0EizHtQLL-ovhHRH98CZ7qXdDPLTYnI59Jn1Y5c0E,926
|
|
96
97
|
cycode/cli/exceptions/handle_scan_errors.py,sha256=1KkBFb7LniflYRr0vMl1FPIZDALPZu1LiXhORGl0jhs,2195
|
|
97
98
|
cycode/cli/files_collector/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
98
|
-
cycode/cli/files_collector/commit_range_documents.py,sha256=
|
|
99
|
+
cycode/cli/files_collector/commit_range_documents.py,sha256=ZAU9er6m8_IF9y9KxZoiEaDOiZC35SEfv5VtqKp4AZc,20484
|
|
99
100
|
cycode/cli/files_collector/documents_walk_ignore.py,sha256=G4e-3vfP4WZ7wa9-VbZ66xCKCioTXnPBfbrs4_hh8xY,4705
|
|
100
101
|
cycode/cli/files_collector/file_excluder.py,sha256=5Y7MM6_4x4FRKCV47D_hOXIg9BzYLHqwoWkmtV7Lt4I,7562
|
|
101
102
|
cycode/cli/files_collector/iac/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -198,8 +199,8 @@ cycode/cyclient/report_client.py,sha256=Scq30NeJPzgXv0hPLO1U05AdE9i_2iu6cIrSKpEJ
|
|
|
198
199
|
cycode/cyclient/scan_client.py,sha256=6TK5FQkfrvV7PHqRnUzEn1PBNd2oPYVamvIixcUfe3c,16755
|
|
199
200
|
cycode/cyclient/scan_config_base.py,sha256=mXsPZGYCtp85rv5GIige40yQZXuRcEKUW-VQJ0vgFzk,1201
|
|
200
201
|
cycode/logger.py,sha256=EfZGRK6VC5rE_LAjIcRrHFiQCueylCDXoG6bvGkrIME,2111
|
|
201
|
-
cycode-3.12.3.
|
|
202
|
-
cycode-3.12.3.
|
|
203
|
-
cycode-3.12.3.
|
|
204
|
-
cycode-3.12.3.
|
|
205
|
-
cycode-3.12.3.
|
|
202
|
+
cycode-3.12.3.dev5.dist-info/METADATA,sha256=LTrKb0Le7JLpLCLUjsO3MRgV3qhsA1w5nh33z1Grles,84350
|
|
203
|
+
cycode-3.12.3.dev5.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
|
|
204
|
+
cycode-3.12.3.dev5.dist-info/entry_points.txt,sha256=iDcVJM8ByLElVgvBgtYxDjw1kT7O8Mo0LcWZIT5L3Ig,45
|
|
205
|
+
cycode-3.12.3.dev5.dist-info/licenses/LICENCE,sha256=2Wx4N6mD_4xB7-E3hPkZ3MPhpJy__k_I8MaCSO-PDRo,1068
|
|
206
|
+
cycode-3.12.3.dev5.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|