chore(security): enhance environment configuration, CI workflows, and wallet daemon with security improvements

- Restructure .env.example with security-focused documentation, service-specific environment file references, and AWS Secrets Manager integration
- Update CLI tests workflow to single Python 3.13 version, add pytest-mock dependency, and consolidate test execution with coverage
- Add comprehensive security validation to package publishing workflow with manual approval gates, secret scanning, and release
This commit is contained in:
oib
2026-03-03 10:33:46 +01:00
parent 00d00cb964
commit f353e00172
220 changed files with 42506 additions and 921 deletions

View File

@@ -727,8 +727,12 @@ def send(ctx, to_address: str, amount: float, description: Optional[str]):
wallet_data["transactions"].append(transaction)
wallet_data["balance"] = balance - amount
with open(wallet_path, "w") as f:
json.dump(wallet_data, f, indent=2)
# Use _save_wallet to preserve encryption
if wallet_data.get("encrypted"):
password = _get_wallet_password(wallet_name)
_save_wallet(wallet_path, wallet_data, password)
else:
_save_wallet(wallet_path, wallet_data)
success(f"Sent {amount} AITBC to {to_address}")
output(
@@ -932,8 +936,7 @@ def unstake(ctx, stake_id: str):
error(f"Wallet '{wallet_name}' not found")
return
with open(wallet_path, "r") as f:
wallet_data = json.load(f)
wallet_data = _load_wallet(wallet_path, wallet_name)
staking = wallet_data.get("staking", [])
stake_record = next(
@@ -1145,13 +1148,85 @@ def multisig_propose(
)
@wallet.command(name="multisig-challenge")
@click.option("--wallet", "wallet_name", required=True, help="Multisig wallet name")
@click.argument("tx_id")
@click.pass_context
def multisig_challenge(ctx, wallet_name: str, tx_id: str):
"""Create a cryptographic challenge for multisig transaction signing"""
wallet_dir = ctx.obj.get("wallet_dir", Path.home() / ".aitbc" / "wallets")
multisig_path = wallet_dir / f"{wallet_name}_multisig.json"
if not multisig_path.exists():
error(f"Multisig wallet '{wallet_name}' not found")
return
with open(multisig_path) as f:
ms_data = json.load(f)
# Find pending transaction
pending = ms_data.get("pending_transactions", [])
tx = next(
(t for t in pending if t["tx_id"] == tx_id and t["status"] == "pending"), None
)
if not tx:
error(f"Pending transaction '{tx_id}' not found")
return
# Import crypto utilities
from ..utils.crypto_utils import multisig_security
try:
# Create signing request
signing_request = multisig_security.create_signing_request(tx, wallet_name)
output({
"tx_id": tx_id,
"wallet": wallet_name,
"challenge": signing_request["challenge"],
"nonce": signing_request["nonce"],
"message": signing_request["message"],
"instructions": [
"1. Copy the challenge string above",
"2. Sign it with your private key using: aitbc wallet sign-challenge <challenge> <private-key>",
"3. Use the returned signature with: aitbc wallet multisig-sign --wallet <wallet> <tx_id> --signer <address> --signature <signature>"
]
}, ctx.obj.get("output_format", "table"))
except Exception as e:
error(f"Failed to create challenge: {e}")
@wallet.command(name="sign-challenge")
@click.argument("challenge")
@click.argument("private_key")
@click.pass_context
def sign_challenge(ctx, challenge: str, private_key: str):
"""Sign a cryptographic challenge (for testing multisig)"""
from ..utils.crypto_utils import sign_challenge
try:
signature = sign_challenge(challenge, private_key)
output({
"challenge": challenge,
"signature": signature,
"message": "Use this signature with multisig-sign command"
}, ctx.obj.get("output_format", "table"))
except Exception as e:
error(f"Failed to sign challenge: {e}")
@wallet.command(name="multisig-sign")
@click.option("--wallet", "wallet_name", required=True, help="Multisig wallet name")
@click.argument("tx_id")
@click.option("--signer", required=True, help="Signer address")
@click.option("--signature", required=True, help="Cryptographic signature (hex)")
@click.pass_context
def multisig_sign(ctx, wallet_name: str, tx_id: str, signer: str):
"""Sign a pending multisig transaction"""
def multisig_sign(ctx, wallet_name: str, tx_id: str, signer: str, signature: str):
"""Sign a pending multisig transaction with cryptographic verification"""
wallet_dir = ctx.obj.get("wallet_dir", Path.home() / ".aitbc" / "wallets")
multisig_path = wallet_dir / f"{wallet_name}_multisig.json"
@@ -1167,6 +1242,16 @@ def multisig_sign(ctx, wallet_name: str, tx_id: str, signer: str):
ctx.exit(1)
return
# Import crypto utilities
from ..utils.crypto_utils import multisig_security
# Verify signature cryptographically
success, message = multisig_security.verify_and_add_signature(tx_id, signature, signer)
if not success:
error(f"Signature verification failed: {message}")
ctx.exit(1)
return
pending = ms_data.get("pending_transactions", [])
tx = next(
(t for t in pending if t["tx_id"] == tx_id and t["status"] == "pending"), None
@@ -1177,11 +1262,21 @@ def multisig_sign(ctx, wallet_name: str, tx_id: str, signer: str):
ctx.exit(1)
return
if signer in tx["signatures"]:
error(f"'{signer}' has already signed this transaction")
return
# Check if already signed
for sig in tx.get("signatures", []):
if sig["signer"] == signer:
error(f"'{signer}' has already signed this transaction")
return
tx["signatures"].append(signer)
# Add cryptographic signature
if "signatures" not in tx:
tx["signatures"] = []
tx["signatures"].append({
"signer": signer,
"signature": signature,
"timestamp": datetime.now().isoformat()
})
# Check if threshold met
if len(tx["signatures"]) >= ms_data["threshold"]: