filanti 1.0.0__py3-none-any.whl → 1.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.
filanti/__init__.py CHANGED
@@ -5,7 +5,7 @@ Provides secure-by-default primitives for file encryption, hashing,
5
5
  and integrity verification.
6
6
  """
7
7
 
8
- __version__ = "1.0.0"
8
+ __version__ = "1.1.0"
9
9
  __author__ = "Decliqe"
10
10
 
11
11
  from filanti.core.errors import (
filanti/api/sdk.py CHANGED
@@ -145,6 +145,9 @@ from filanti.core.secrets import (
145
145
  resolve_secret,
146
146
  resolve_secret_optional,
147
147
  is_env_reference,
148
+ get_env_var_name,
149
+ load_dotenv,
150
+ get_supported_patterns,
148
151
  redact_secret,
149
152
  redact_secrets,
150
153
  create_safe_json_output,
@@ -215,6 +218,36 @@ class Filanti:
215
218
  use the underlying modules directly.
216
219
  """
217
220
 
221
+ # =========================================================================
222
+ # SECRETS & ENVIRONMENT
223
+ # =========================================================================
224
+
225
+ @staticmethod
226
+ def load_dotenv(
227
+ path: str | Path = ".env",
228
+ override: bool = False,
229
+ ) -> dict[str, str]:
230
+ """Load environment variables from a .env file.
231
+
232
+ Loads variables from the specified .env file into the environment.
233
+ This is useful for loading secrets before encryption/decryption.
234
+
235
+ Args:
236
+ path: Path to .env file (default: ".env" in current directory).
237
+ override: If True, override existing environment variables.
238
+
239
+ Returns:
240
+ Dictionary of loaded variables (name -> value).
241
+
242
+ Example:
243
+ # Load secrets from .env
244
+ Filanti.load_dotenv(".env")
245
+
246
+ # Now use ENV references
247
+ Filanti.encrypt("file.txt", password="ENV:MY_PASSWORD")
248
+ """
249
+ return load_dotenv(path, override=override)
250
+
218
251
  # =========================================================================
219
252
  # HASHING
220
253
  # =========================================================================
@@ -308,20 +341,30 @@ class Filanti:
308
341
  key: bytes | None = None,
309
342
  output: str | Path | None = None,
310
343
  algorithm: str = "aes-256-gcm",
344
+ remove_source: bool = False,
345
+ secure_delete: bool = True,
346
+ dotenv_path: str | Path | None = None,
311
347
  ) -> EncryptResult:
312
348
  """Encrypt a file.
313
349
 
314
350
  Provide either password OR key, not both.
315
351
 
316
- Supports ENV-based secret resolution for password:
352
+ Supports multiple secret reference formats:
317
353
  Filanti.encrypt("file.txt", password="ENV:MY_PASSWORD")
354
+ Filanti.encrypt("file.txt", password="$env:MY_PASSWORD") # PowerShell
355
+ Filanti.encrypt("file.txt", password="${MY_PASSWORD}") # Shell-style
356
+ Filanti.encrypt("file.txt", password="env.MY_PASSWORD") # Dot notation
318
357
 
319
358
  Args:
320
359
  path: Path to file to encrypt.
321
- password: Password for key derivation. Supports ENV:VAR_NAME syntax.
360
+ password: Password for key derivation. Supports ENV reference syntax.
322
361
  key: Raw encryption key (32 bytes for AES-256).
323
362
  output: Output path (default: path + .enc).
324
363
  algorithm: Encryption algorithm.
364
+ remove_source: If True, delete original file after successful encryption.
365
+ secure_delete: If True and remove_source is True, securely overwrite
366
+ the original file before deletion.
367
+ dotenv_path: Optional path to .env file to load secrets from.
325
368
 
326
369
  Returns:
327
370
  EncryptResult with output path and metadata.
@@ -335,15 +378,23 @@ class Filanti:
335
378
 
336
379
  if password:
337
380
  # Resolve password from ENV if needed
338
- resolved_password = resolve_secret(password)
339
- metadata = encrypt_file_with_password(path, out_path, resolved_password, alg)
381
+ resolved_password = resolve_secret(password, dotenv_path=dotenv_path)
382
+ metadata = encrypt_file_with_password(
383
+ path, out_path, resolved_password, alg,
384
+ remove_source=remove_source,
385
+ secure_delete=secure_delete,
386
+ )
340
387
  return EncryptResult(
341
388
  output_path=out_path,
342
389
  algorithm=metadata.algorithm,
343
390
  kdf_algorithm=metadata.kdf_algorithm,
344
391
  )
345
392
  elif key:
346
- encrypt_file(path, out_path, key, alg)
393
+ encrypt_file(
394
+ path, out_path, key, alg,
395
+ remove_source=remove_source,
396
+ secure_delete=secure_delete,
397
+ )
347
398
  return EncryptResult(output_path=out_path, algorithm=algorithm)
348
399
  else:
349
400
  raise ValueError("Must provide either password or key")
@@ -354,19 +405,29 @@ class Filanti:
354
405
  password: str | None = None,
355
406
  key: bytes | None = None,
356
407
  output: str | Path | None = None,
408
+ dotenv_path: str | Path | None = None,
409
+ remove_source: bool = False,
410
+ secure_delete: bool = True,
357
411
  ) -> DecryptResult:
358
412
  """Decrypt a file.
359
413
 
360
414
  Provide either password OR key, not both.
361
415
 
362
- Supports ENV-based secret resolution for password:
416
+ Supports multiple secret reference formats:
363
417
  Filanti.decrypt("file.enc", password="ENV:MY_PASSWORD")
418
+ Filanti.decrypt("file.enc", password="$env:MY_PASSWORD") # PowerShell
419
+ Filanti.decrypt("file.enc", password="${MY_PASSWORD}") # Shell-style
420
+ Filanti.decrypt("file.enc", password="env.MY_PASSWORD") # Dot notation
364
421
 
365
422
  Args:
366
423
  path: Path to encrypted file.
367
- password: Password used for encryption. Supports ENV:VAR_NAME syntax.
424
+ password: Password used for encryption. Supports ENV reference syntax.
368
425
  key: Raw encryption key.
369
426
  output: Output path (default: removes .enc extension).
427
+ dotenv_path: Optional path to .env file to load secrets from.
428
+ remove_source: If True, delete encrypted file after successful decryption.
429
+ secure_delete: If True and remove_source is True, securely overwrite
430
+ the encrypted file before deletion.
370
431
 
371
432
  Returns:
372
433
  DecryptResult with output path and size.
@@ -384,13 +445,22 @@ class Filanti:
384
445
 
385
446
  if password:
386
447
  # Resolve password from ENV if needed
387
- resolved_password = resolve_secret(password)
448
+ resolved_password = resolve_secret(password, dotenv_path=dotenv_path)
388
449
  size = decrypt_file_with_password(path, out_path, resolved_password)
389
450
  elif key:
390
451
  size = decrypt_file(path, out_path, key)
391
452
  else:
392
453
  raise ValueError("Must provide either password or key")
393
454
 
455
+ # Remove encrypted source file if requested
456
+ if remove_source:
457
+ from filanti.core.file_manager import get_file_manager
458
+ fm = get_file_manager()
459
+ if secure_delete:
460
+ fm.secure_delete(path)
461
+ else:
462
+ fm.delete(path)
463
+
394
464
  return DecryptResult(output_path=out_path, size=size)
395
465
 
396
466
  @staticmethod
@@ -399,17 +469,22 @@ class Filanti:
399
469
  password: str | None = None,
400
470
  key: bytes | None = None,
401
471
  algorithm: str = "aes-256-gcm",
472
+ dotenv_path: str | Path | None = None,
402
473
  ) -> bytes:
403
474
  """Encrypt bytes data.
404
475
 
405
- Supports ENV-based secret resolution for password:
476
+ Supports multiple secret reference formats:
406
477
  Filanti.encrypt_bytes(data, password="ENV:MY_PASSWORD")
478
+ Filanti.encrypt_bytes(data, password="$env:MY_PASSWORD") # PowerShell
479
+ Filanti.encrypt_bytes(data, password="${MY_PASSWORD}") # Shell-style
480
+ Filanti.encrypt_bytes(data, password="env.MY_PASSWORD") # Dot notation
407
481
 
408
482
  Args:
409
483
  data: Data to encrypt.
410
- password: Password for key derivation. Supports ENV:VAR_NAME syntax.
484
+ password: Password for key derivation. Supports ENV reference syntax.
411
485
  key: Raw encryption key.
412
486
  algorithm: Encryption algorithm.
487
+ dotenv_path: Optional path to .env file to load secrets from.
413
488
 
414
489
  Returns:
415
490
  Encrypted bytes (includes nonce and auth tag).
@@ -421,7 +496,7 @@ class Filanti:
421
496
 
422
497
  if password is not None:
423
498
  # Resolve password from ENV if needed
424
- resolved_password = resolve_secret(password)
499
+ resolved_password = resolve_secret(password, dotenv_path=dotenv_path)
425
500
  result = _encrypt_bytes_with_password(data, resolved_password, alg)
426
501
  return result.to_bytes()
427
502
  elif key is not None:
@@ -436,16 +511,21 @@ class Filanti:
436
511
  data: bytes,
437
512
  password: str | None = None,
438
513
  key: bytes | None = None,
514
+ dotenv_path: str | Path | None = None,
439
515
  ) -> bytes:
440
516
  """Decrypt bytes data.
441
517
 
442
- Supports ENV-based secret resolution for password:
518
+ Supports multiple secret reference formats:
443
519
  Filanti.decrypt_bytes(data, password="ENV:MY_PASSWORD")
520
+ Filanti.decrypt_bytes(data, password="$env:MY_PASSWORD") # PowerShell
521
+ Filanti.decrypt_bytes(data, password="${MY_PASSWORD}") # Shell-style
522
+ Filanti.decrypt_bytes(data, password="env.MY_PASSWORD") # Dot notation
444
523
 
445
524
  Args:
446
525
  data: Encrypted data.
447
- password: Password used for encryption. Supports ENV:VAR_NAME syntax.
526
+ password: Password used for encryption. Supports ENV reference syntax.
448
527
  key: Raw encryption key.
528
+ dotenv_path: Optional path to .env file to load secrets from.
449
529
 
450
530
  Returns:
451
531
  Decrypted bytes.
@@ -455,7 +535,7 @@ class Filanti:
455
535
  """
456
536
  if password is not None:
457
537
  # Resolve password from ENV if needed
458
- resolved_password = resolve_secret(password)
538
+ resolved_password = resolve_secret(password, dotenv_path=dotenv_path)
459
539
  # Parse the encrypted data from bytes
460
540
  encrypted = EncryptedData.from_bytes(data)
461
541
  return _decrypt_bytes_with_password(encrypted, resolved_password)
@@ -920,6 +1000,8 @@ class Filanti:
920
1000
  output: str | Path | None = None,
921
1001
  password: str | bytes | None = None,
922
1002
  recipient_id: str | None = None,
1003
+ remove_source: bool = False,
1004
+ secure_delete: bool = True,
923
1005
  ) -> HybridDecryptResult:
924
1006
  """Decrypt a hybrid encrypted file.
925
1007
 
@@ -929,6 +1011,9 @@ class Filanti:
929
1011
  output: Output path (default: removes .henc extension).
930
1012
  password: Password if private key is encrypted.
931
1013
  recipient_id: Optional recipient ID to select specific session key.
1014
+ remove_source: If True, delete encrypted file after successful decryption.
1015
+ secure_delete: If True and remove_source is True, securely overwrite
1016
+ the encrypted file before deletion.
932
1017
 
933
1018
  Returns:
934
1019
  HybridDecryptResult with output path and size.
@@ -943,6 +1028,13 @@ class Filanti:
943
1028
  "my-key.pem",
944
1029
  password="my-password"
945
1030
  )
1031
+
1032
+ # Decrypt and remove encrypted file
1033
+ Filanti.hybrid_decrypt(
1034
+ "secret.txt.henc",
1035
+ "my-key.pem",
1036
+ remove_source=True
1037
+ )
946
1038
  """
947
1039
  path = Path(path)
948
1040
  if output:
@@ -957,6 +1049,15 @@ class Filanti:
957
1049
 
958
1050
  size = _hybrid_decrypt_file(path, out_path, private_key, password, recipient_id)
959
1051
 
1052
+ # Remove encrypted source file if requested
1053
+ if remove_source:
1054
+ from filanti.core.file_manager import get_file_manager
1055
+ fm = get_file_manager()
1056
+ if secure_delete:
1057
+ fm.secure_delete(path)
1058
+ else:
1059
+ fm.delete(path)
1060
+
960
1061
  return HybridDecryptResult(output_path=out_path, size=size)
961
1062
 
962
1063
  @staticmethod