Skip to content
This repository was archived by the owner on Jun 5, 2025. It is now read-only.

Commit c3eff9c

Browse files
authored
Merge pull request #298 from stacklok/fix-tests
Fix Unit Tests
2 parents 596f998 + 74c89c5 commit c3eff9c

File tree

8 files changed

+723
-648
lines changed

8 files changed

+723
-648
lines changed

cert_gen.py

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
import datetime
2+
import os
3+
4+
from cryptography import x509
5+
from cryptography.hazmat.primitives import hashes, serialization
6+
from cryptography.hazmat.primitives.asymmetric import rsa
7+
from cryptography.x509.oid import ExtendedKeyUsageOID, NameOID
8+
9+
10+
def generate_certificates(cert_dir="certs"):
11+
"""Generate self-signed certificates with proper extensions for HTTPS proxy"""
12+
# Create certificates directory if it doesn't exist
13+
if not os.path.exists(cert_dir):
14+
print("Making: ", cert_dir)
15+
os.makedirs(cert_dir)
16+
17+
# Generate private key
18+
ca_private_key = rsa.generate_private_key(
19+
public_exponent=65537,
20+
key_size=4096, # Increased key size for better security
21+
)
22+
23+
# Generate public key
24+
ca_public_key = ca_private_key.public_key()
25+
26+
# CA BEGIN
27+
name = x509.Name(
28+
[
29+
x509.NameAttribute(NameOID.COMMON_NAME, "Proxy Pilot CA"),
30+
x509.NameAttribute(NameOID.ORGANIZATION_NAME, "Proxy Pilot"),
31+
x509.NameAttribute(NameOID.ORGANIZATIONAL_UNIT_NAME, "Development"),
32+
x509.NameAttribute(NameOID.COUNTRY_NAME, "UK"),
33+
]
34+
)
35+
36+
builder = x509.CertificateBuilder()
37+
builder = builder.subject_name(name)
38+
builder = builder.issuer_name(name)
39+
builder = builder.public_key(ca_public_key)
40+
builder = builder.serial_number(x509.random_serial_number())
41+
builder = builder.not_valid_before(datetime.datetime.utcnow())
42+
builder = builder.not_valid_after(
43+
datetime.datetime.utcnow() + datetime.timedelta(days=3650) # 10 years
44+
)
45+
46+
builder = builder.add_extension(
47+
x509.BasicConstraints(ca=True, path_length=None),
48+
critical=True,
49+
)
50+
51+
builder = builder.add_extension(
52+
x509.KeyUsage(
53+
digital_signature=True,
54+
content_commitment=False,
55+
key_encipherment=True,
56+
data_encipherment=False,
57+
key_agreement=False,
58+
key_cert_sign=True, # This is a CA
59+
crl_sign=True,
60+
encipher_only=False,
61+
decipher_only=False,
62+
),
63+
critical=True,
64+
)
65+
66+
ca_cert = builder.sign(
67+
private_key=ca_private_key,
68+
algorithm=hashes.SHA256(),
69+
)
70+
71+
# Save CA certificate and key
72+
73+
with open("certs/ca.crt", "wb") as f:
74+
f.write(ca_cert.public_bytes(serialization.Encoding.PEM))
75+
76+
with open("certs/ca.key", "wb") as f:
77+
f.write(
78+
ca_private_key.private_bytes(
79+
encoding=serialization.Encoding.PEM,
80+
format=serialization.PrivateFormat.PKCS8,
81+
encryption_algorithm=serialization.NoEncryption(),
82+
)
83+
)
84+
# CA END
85+
86+
# SERVER BEGIN
87+
88+
# Generate new certificate for domain
89+
server_key = rsa.generate_private_key(
90+
public_exponent=65537,
91+
key_size=2048, # 2048 bits is sufficient for domain certs
92+
)
93+
94+
name = x509.Name(
95+
[
96+
x509.NameAttribute(NameOID.COMMON_NAME, "Proxy Pilot CA"),
97+
x509.NameAttribute(NameOID.ORGANIZATION_NAME, "Proxy Pilot Generated"),
98+
]
99+
)
100+
101+
builder = x509.CertificateBuilder()
102+
builder = builder.subject_name(name)
103+
builder = builder.issuer_name(ca_cert.subject)
104+
builder = builder.public_key(server_key.public_key())
105+
builder = builder.serial_number(x509.random_serial_number())
106+
builder = builder.not_valid_before(datetime.datetime.utcnow())
107+
builder = builder.not_valid_after(datetime.datetime.utcnow() + datetime.timedelta(days=365))
108+
109+
# Add domain to SAN
110+
builder = builder.add_extension(
111+
x509.SubjectAlternativeName([x509.DNSName("localhost")]),
112+
critical=False,
113+
)
114+
115+
# Add extended key usage
116+
builder = builder.add_extension(
117+
x509.ExtendedKeyUsage(
118+
[
119+
ExtendedKeyUsageOID.SERVER_AUTH,
120+
ExtendedKeyUsageOID.CLIENT_AUTH,
121+
]
122+
),
123+
critical=False,
124+
)
125+
126+
# Basic constraints (not a CA)
127+
builder = builder.add_extension(
128+
x509.BasicConstraints(ca=False, path_length=None),
129+
critical=True,
130+
)
131+
132+
certificate = builder.sign(
133+
private_key=ca_private_key,
134+
algorithm=hashes.SHA256(),
135+
)
136+
137+
with open("certs/server.crt", "wb") as f:
138+
f.write(certificate.public_bytes(serialization.Encoding.PEM))
139+
140+
with open("certs/server.key", "wb") as f:
141+
f.write(
142+
server_key.private_bytes(
143+
encoding=serialization.Encoding.PEM,
144+
format=serialization.PrivateFormat.PKCS8,
145+
encryption_algorithm=serialization.NoEncryption(),
146+
)
147+
)
148+
149+
print("Certificates generated successfully in the 'certs' directory")
150+
print("\nTo trust these certificates:")
151+
print("\nOn macOS:")
152+
print(
153+
"sudo security add-trusted-cert -d -r trustRoot "
154+
"-k /Library/Keychains/System.keychain certs/server.crt"
155+
)
156+
print("\nOn Windows (PowerShell as Admin):")
157+
print(
158+
'Import-Certificate -FilePath "certs\\server.crt" '
159+
"-CertStoreLocation Cert:\\LocalMachine\\Root"
160+
)
161+
print("\nOn Linux:")
162+
print("sudo cp certs/server.crt /usr/local/share/ca-certificates/proxy-pilot.crt")
163+
print("sudo update-ca-certificates")
164+
print("\nFor VSCode, add to settings.json:")
165+
print(
166+
"""{
167+
"http.proxy": "https://localhost:8989",
168+
"http.proxySupport": "on",
169+
"github.copilot.advanced": {
170+
"debug.testOverrideProxyUrl": "https://localhost:8989",
171+
"debug.overrideProxyUrl": "https://localhost:8989"
172+
}
173+
}"""
174+
)
175+
176+
177+
if __name__ == "__main__":
178+
generate_certificates()

src/codegate/ca/codegate_ca.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -349,7 +349,6 @@ def generate_certificates(self) -> Tuple[str, str]:
349349
algorithm=hashes.SHA256(),
350350
)
351351

352-
# os.path.join(Config.get_config().server_key)
353352
with open(
354353
os.path.join(Config.get_config().certs_dir, Config.get_config().server_cert), "wb"
355354
) as f:

src/codegate/cli.py

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"""Command-line interface for codegate."""
22

33
import asyncio
4+
import signal
45
import sys
56
from pathlib import Path
67
from typing import Dict, Optional
@@ -18,8 +19,6 @@
1819
from codegate.server import init_app
1920
from codegate.storage.utils import restore_storage_backup
2021

21-
logger = structlog.get_logger("codegate")
22-
2322

2423
class UvicornServer:
2524
def __init__(self, config: UvicornConfig, server: Server):
@@ -32,10 +31,16 @@ def __init__(self, config: UvicornConfig, server: Server):
3231
self._startup_complete = asyncio.Event()
3332
self._shutdown_event = asyncio.Event()
3433
self._should_exit = False
34+
self.logger = structlog.get_logger("codegate")
3535

3636
async def serve(self) -> None:
3737
"""Start the uvicorn server and handle shutdown gracefully."""
38-
logger.debug(f"Starting server on {self.host}:{self.port}")
38+
self.logger.debug(f"Starting server on {self.host}:{self.port}")
39+
40+
# Set up signal handlers
41+
loop = asyncio.get_running_loop()
42+
loop.add_signal_handler(signal.SIGTERM, lambda: asyncio.create_task(self.cleanup()))
43+
loop.add_signal_handler(signal.SIGINT, lambda: asyncio.create_task(self.cleanup()))
3944

4045
self.server = Server(config=self.config)
4146
self.server.force_exit = True
@@ -44,40 +49,41 @@ async def serve(self) -> None:
4449
self._startup_complete.set()
4550
await self.server.serve()
4651
except asyncio.CancelledError:
47-
logger.info("Server received cancellation")
52+
self.logger.info("Server received cancellation")
4853
except Exception as e:
49-
logger.exception("Unexpected error occurred during server execution", exc_info=e)
54+
self.logger.exception("Unexpected error occurred during server execution", exc_info=e)
5055
finally:
5156
await self.cleanup()
5257

5358
async def wait_startup_complete(self) -> None:
5459
"""Wait for the server to complete startup."""
55-
logger.debug("Waiting for server startup to complete")
60+
self.logger.debug("Waiting for server startup to complete")
5661
await self._startup_complete.wait()
5762

5863
async def cleanup(self) -> None:
5964
"""Cleanup server resources and ensure graceful shutdown."""
60-
logger.debug("Cleaning up server resources")
65+
self.logger.debug("Cleaning up server resources")
6166
if not self._should_exit:
6267
self._should_exit = True
63-
logger.debug("Initiating server shutdown")
68+
self.logger.debug("Initiating server shutdown")
6469
self._shutdown_event.set()
6570

6671
if hasattr(self.server, "shutdown"):
67-
logger.debug("Shutting down server")
72+
self.logger.debug("Shutting down server")
6873
await self.server.shutdown()
6974

7075
# Ensure all connections are closed
7176
tasks = [t for t in asyncio.all_tasks() if t is not asyncio.current_task()]
7277
[task.cancel() for task in tasks]
7378

7479
await asyncio.gather(*tasks, return_exceptions=True)
75-
logger.debug("Server shutdown complete")
80+
self.logger.debug("Server shutdown complete")
7681

7782

7883
def validate_port(ctx: click.Context, param: click.Parameter, value: int) -> int:
79-
logger.debug(f"Validating port number: {value}")
8084
"""Validate the port number is in valid range."""
85+
logger = structlog.get_logger("codegate")
86+
logger.debug(f"Validating port number: {value}")
8187
if value is not None and not (1 <= value <= 65535):
8288
raise click.BadParameter("Port must be between 1 and 65535")
8389
return value
@@ -286,10 +292,14 @@ def serve(
286292
db_path=db_path,
287293
)
288294

295+
# Set up logging first
296+
setup_logging(cfg.log_level, cfg.log_format)
297+
logger = structlog.get_logger("codegate")
298+
289299
init_db_sync(cfg.db_path)
290300

291301
# Check certificates and create CA if necessary
292-
logger.info("Checking certificates and creating CA our created")
302+
logger.info("Checking certificates and creating CA if needed")
293303
ca = CertificateAuthority.get_instance()
294304
ca.ensure_certificates_exist()
295305

@@ -311,16 +321,15 @@ def serve(
311321
click.echo(f"Configuration error: {e}", err=True)
312322
sys.exit(1)
313323
except Exception as e:
314-
if logger:
315-
logger.exception("Unexpected error occurred")
324+
logger = structlog.get_logger("codegate")
325+
logger.exception("Unexpected error occurred")
316326
click.echo(f"Error: {e}", err=True)
317327
sys.exit(1)
318328

319329

320330
async def run_servers(cfg: Config, app) -> None:
321331
"""Run the codegate server."""
322332
try:
323-
setup_logging(cfg.log_level, cfg.log_format)
324333
logger = structlog.get_logger("codegate")
325334
logger.info(
326335
"Starting server",

src/codegate/pipeline/secrets/signatures.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -175,8 +175,10 @@ def _load_signatures(cls) -> None:
175175
yaml_data = cls._load_yaml(cls._yaml_path)
176176

177177
# Add custom GitHub token patterns
178-
github_patterns = {"Access Token": r"ghp_[0-9a-zA-Z]{32}",
179-
"Personal Token": r"github_pat_[a-zA-Z0-9]{22}_[a-zA-Z0-9]{59}"}
178+
github_patterns = {
179+
"Access Token": r"ghp_[0-9a-zA-Z]{32}",
180+
"Personal Token": r"github_pat_[a-zA-Z0-9]{22}_[a-zA-Z0-9]{59}",
181+
}
180182
cls._add_signature_group("GitHub", github_patterns)
181183

182184
# Process patterns from YAML

0 commit comments

Comments
 (0)