aiwaf 0.1.9.1.3__py3-none-any.whl → 0.1.9.1.5__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.
Potentially problematic release.
This version of aiwaf might be problematic. Click here for more details.
- aiwaf/__init__.py +1 -1
- aiwaf/management/commands/check_dependencies.py +374 -12
- {aiwaf-0.1.9.1.3.dist-info → aiwaf-0.1.9.1.5.dist-info}/METADATA +175 -4
- {aiwaf-0.1.9.1.3.dist-info → aiwaf-0.1.9.1.5.dist-info}/RECORD +7 -7
- {aiwaf-0.1.9.1.3.dist-info → aiwaf-0.1.9.1.5.dist-info}/WHEEL +0 -0
- {aiwaf-0.1.9.1.3.dist-info → aiwaf-0.1.9.1.5.dist-info}/licenses/LICENSE +0 -0
- {aiwaf-0.1.9.1.3.dist-info → aiwaf-0.1.9.1.5.dist-info}/top_level.txt +0 -0
aiwaf/__init__.py
CHANGED
|
@@ -30,6 +30,21 @@ class Command(BaseCommand):
|
|
|
30
30
|
default=True,
|
|
31
31
|
help='Check for package compatibility issues (default: True)'
|
|
32
32
|
)
|
|
33
|
+
parser.add_argument(
|
|
34
|
+
'--upgrade',
|
|
35
|
+
action='store_true',
|
|
36
|
+
help='Automatically upgrade packages while maintaining stability'
|
|
37
|
+
)
|
|
38
|
+
parser.add_argument(
|
|
39
|
+
'--dry-run',
|
|
40
|
+
action='store_true',
|
|
41
|
+
help='Show what would be upgraded without actually upgrading'
|
|
42
|
+
)
|
|
43
|
+
parser.add_argument(
|
|
44
|
+
'--update-requirements',
|
|
45
|
+
action='store_true',
|
|
46
|
+
help='Update requirements.txt with upgraded package versions'
|
|
47
|
+
)
|
|
33
48
|
|
|
34
49
|
def handle(self, *args, **options):
|
|
35
50
|
self.stdout.write(self.style.SUCCESS('🔍 Checking project dependencies...\n'))
|
|
@@ -50,6 +65,12 @@ class Command(BaseCommand):
|
|
|
50
65
|
if options['check_compatibility']:
|
|
51
66
|
self.check_compatibility(results)
|
|
52
67
|
|
|
68
|
+
if options['upgrade']:
|
|
69
|
+
upgraded_packages = self.perform_safe_upgrade(results, options['dry_run'])
|
|
70
|
+
|
|
71
|
+
if options['update_requirements'] and upgraded_packages and not options['dry_run']:
|
|
72
|
+
self.update_requirements_file(upgraded_packages)
|
|
73
|
+
|
|
53
74
|
if options['check_security']:
|
|
54
75
|
self.check_security_vulnerabilities(dependencies)
|
|
55
76
|
|
|
@@ -338,18 +359,6 @@ class Command(BaseCommand):
|
|
|
338
359
|
'severity': 'error'
|
|
339
360
|
}
|
|
340
361
|
]
|
|
341
|
-
},
|
|
342
|
-
{
|
|
343
|
-
'package': 'django',
|
|
344
|
-
'conflicts_with': [
|
|
345
|
-
{
|
|
346
|
-
'package': 'pandas',
|
|
347
|
-
'django_versions': '>=4.0',
|
|
348
|
-
'pandas_versions': '<1.3',
|
|
349
|
-
'message': 'Django 4.0+ works better with pandas 1.3+',
|
|
350
|
-
'severity': 'warning'
|
|
351
|
-
}
|
|
352
|
-
]
|
|
353
362
|
}
|
|
354
363
|
]
|
|
355
364
|
|
|
@@ -433,6 +442,359 @@ class Command(BaseCommand):
|
|
|
433
442
|
|
|
434
443
|
return conflicts
|
|
435
444
|
|
|
445
|
+
def perform_safe_upgrade(self, results, dry_run=False):
|
|
446
|
+
"""Perform safe package upgrades while maintaining AIWAF compatibility"""
|
|
447
|
+
self.stdout.write(self.style.HTTP_INFO("\n🔄 Planning safe package upgrades..."))
|
|
448
|
+
|
|
449
|
+
# AIWAF compatibility constraints
|
|
450
|
+
aiwaf_constraints = self.get_aiwaf_compatibility_constraints()
|
|
451
|
+
|
|
452
|
+
# Get packages that can be safely upgraded
|
|
453
|
+
safe_upgrades = []
|
|
454
|
+
blocked_upgrades = []
|
|
455
|
+
upgraded_packages = [] # Track successfully upgraded packages
|
|
456
|
+
|
|
457
|
+
for pkg in results:
|
|
458
|
+
if pkg['status'] == 'outdated' and pkg['name'].lower() != 'aiwaf':
|
|
459
|
+
upgrade_plan = self.plan_safe_upgrade(pkg, aiwaf_constraints, results)
|
|
460
|
+
if upgrade_plan['safe']:
|
|
461
|
+
safe_upgrades.append(upgrade_plan)
|
|
462
|
+
else:
|
|
463
|
+
blocked_upgrades.append(upgrade_plan)
|
|
464
|
+
|
|
465
|
+
# Display upgrade plan
|
|
466
|
+
if safe_upgrades:
|
|
467
|
+
self.stdout.write(self.style.SUCCESS("\n✅ SAFE UPGRADES PLANNED:"))
|
|
468
|
+
self.stdout.write("─" * 80)
|
|
469
|
+
for upgrade in safe_upgrades:
|
|
470
|
+
pkg = upgrade['package']
|
|
471
|
+
target_version = upgrade['target_version']
|
|
472
|
+
self.stdout.write(
|
|
473
|
+
f"📦 {pkg['name']:<20} {pkg['installed']:<12} → {target_version:<12} "
|
|
474
|
+
f"(Latest: {pkg['latest']})"
|
|
475
|
+
)
|
|
476
|
+
if upgrade['reason']:
|
|
477
|
+
self.stdout.write(f" 💡 {upgrade['reason']}")
|
|
478
|
+
|
|
479
|
+
if blocked_upgrades:
|
|
480
|
+
self.stdout.write(self.style.WARNING("\n⚠️ UPGRADES BLOCKED FOR STABILITY:"))
|
|
481
|
+
self.stdout.write("─" * 80)
|
|
482
|
+
for upgrade in blocked_upgrades:
|
|
483
|
+
pkg = upgrade['package']
|
|
484
|
+
self.stdout.write(
|
|
485
|
+
f"❌ {pkg['name']:<20} {pkg['installed']:<12} ✗ {pkg['latest']:<12}"
|
|
486
|
+
)
|
|
487
|
+
self.stdout.write(f" 🚨 {upgrade['reason']}")
|
|
488
|
+
|
|
489
|
+
if not safe_upgrades:
|
|
490
|
+
self.stdout.write(self.style.NOTICE("ℹ️ No safe upgrades available at this time."))
|
|
491
|
+
return []
|
|
492
|
+
|
|
493
|
+
# Execute upgrades
|
|
494
|
+
if dry_run:
|
|
495
|
+
self.stdout.write(self.style.NOTICE("\n🏃 DRY RUN MODE - No packages will be upgraded"))
|
|
496
|
+
upgrade_cmd = "pip install --upgrade " + " ".join([
|
|
497
|
+
f"{u['package']['name']}=={u['target_version']}" if u['target_version'] != u['package']['latest']
|
|
498
|
+
else u['package']['name']
|
|
499
|
+
for u in safe_upgrades
|
|
500
|
+
])
|
|
501
|
+
self.stdout.write(f"Command that would be executed:\n {upgrade_cmd}")
|
|
502
|
+
return []
|
|
503
|
+
return []
|
|
504
|
+
else:
|
|
505
|
+
self.stdout.write(self.style.HTTP_INFO("\n🚀 Executing safe upgrades..."))
|
|
506
|
+
success_count = 0
|
|
507
|
+
|
|
508
|
+
for upgrade in safe_upgrades:
|
|
509
|
+
pkg_name = upgrade['package']['name']
|
|
510
|
+
target_version = upgrade['target_version']
|
|
511
|
+
|
|
512
|
+
try:
|
|
513
|
+
if target_version == upgrade['package']['latest']:
|
|
514
|
+
cmd = ['pip', 'install', '--upgrade', pkg_name]
|
|
515
|
+
else:
|
|
516
|
+
cmd = ['pip', 'install', f"{pkg_name}=={target_version}"]
|
|
517
|
+
|
|
518
|
+
self.stdout.write(f" Upgrading {pkg_name}...")
|
|
519
|
+
result = subprocess.run(cmd, capture_output=True, text=True, timeout=300)
|
|
520
|
+
|
|
521
|
+
if result.returncode == 0:
|
|
522
|
+
self.stdout.write(self.style.SUCCESS(f" ✅ {pkg_name} upgraded successfully"))
|
|
523
|
+
success_count += 1
|
|
524
|
+
upgraded_packages.append({
|
|
525
|
+
'name': pkg_name,
|
|
526
|
+
'old_version': upgrade['package']['installed'],
|
|
527
|
+
'new_version': target_version
|
|
528
|
+
})
|
|
529
|
+
else:
|
|
530
|
+
self.stdout.write(self.style.ERROR(f" ❌ Failed to upgrade {pkg_name}: {result.stderr}"))
|
|
531
|
+
|
|
532
|
+
except subprocess.TimeoutExpired:
|
|
533
|
+
self.stdout.write(self.style.ERROR(f" ❌ Timeout upgrading {pkg_name}"))
|
|
534
|
+
except Exception as e:
|
|
535
|
+
self.stdout.write(self.style.ERROR(f" ❌ Error upgrading {pkg_name}: {e}"))
|
|
536
|
+
|
|
537
|
+
self.stdout.write(f"\n🎉 Upgrade complete: {success_count}/{len(safe_upgrades)} packages upgraded successfully")
|
|
538
|
+
|
|
539
|
+
if success_count > 0:
|
|
540
|
+
self.stdout.write(self.style.HTTP_INFO("\n💡 Recommendations after upgrade:"))
|
|
541
|
+
self.stdout.write(" 1. Run tests to ensure everything works correctly")
|
|
542
|
+
self.stdout.write(" 2. Run 'python manage.py check_dependencies' again to verify")
|
|
543
|
+
self.stdout.write(" 3. Consider running 'python manage.py detect_and_train' to retrain with new packages")
|
|
544
|
+
|
|
545
|
+
return upgraded_packages
|
|
546
|
+
|
|
547
|
+
def get_aiwaf_compatibility_constraints(self):
|
|
548
|
+
"""Get AIWAF's compatibility constraints to ensure stability"""
|
|
549
|
+
return {
|
|
550
|
+
'django': {
|
|
551
|
+
'min_version': '3.2',
|
|
552
|
+
'max_version': '99.0', # AIWAF works with all Django versions
|
|
553
|
+
'reason': 'AIWAF is compatible with Django 3.2+'
|
|
554
|
+
},
|
|
555
|
+
'numpy': {
|
|
556
|
+
'min_version': '1.21',
|
|
557
|
+
'max_version': '1.99.99', # Avoid NumPy 2.0 breaking changes
|
|
558
|
+
'reason': 'NumPy 2.0+ may cause compatibility issues'
|
|
559
|
+
},
|
|
560
|
+
'pandas': {
|
|
561
|
+
'min_version': '1.3',
|
|
562
|
+
'max_version': '2.9.99',
|
|
563
|
+
'reason': 'AIWAF tested with pandas 1.3-2.x series'
|
|
564
|
+
},
|
|
565
|
+
'scikit-learn': {
|
|
566
|
+
'min_version': '1.0',
|
|
567
|
+
'max_version': '1.99.99', # Stay in 1.x series
|
|
568
|
+
'reason': 'AIWAF models trained with scikit-learn 1.x'
|
|
569
|
+
},
|
|
570
|
+
'joblib': {
|
|
571
|
+
'min_version': '1.1',
|
|
572
|
+
'max_version': '1.99.99',
|
|
573
|
+
'reason': 'AIWAF compatible with joblib 1.x series'
|
|
574
|
+
},
|
|
575
|
+
'packaging': {
|
|
576
|
+
'min_version': '21.0',
|
|
577
|
+
'max_version': '99.0',
|
|
578
|
+
'reason': 'Required for dependency checking'
|
|
579
|
+
},
|
|
580
|
+
'requests': {
|
|
581
|
+
'min_version': '2.25.0',
|
|
582
|
+
'max_version': '2.99.99',
|
|
583
|
+
'reason': 'Required for PyPI API access'
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
def plan_safe_upgrade(self, pkg, aiwaf_constraints, all_results):
|
|
588
|
+
"""Plan a safe upgrade for a package considering AIWAF compatibility"""
|
|
589
|
+
pkg_name = pkg['name'].lower()
|
|
590
|
+
current_version = pkg['installed']
|
|
591
|
+
latest_version = pkg['latest']
|
|
592
|
+
|
|
593
|
+
# Skip AIWAF itself
|
|
594
|
+
if pkg_name == 'aiwaf':
|
|
595
|
+
return {
|
|
596
|
+
'package': pkg,
|
|
597
|
+
'safe': False,
|
|
598
|
+
'target_version': None,
|
|
599
|
+
'reason': 'AIWAF should be upgraded separately using pip install --upgrade aiwaf'
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
# Check AIWAF constraints
|
|
603
|
+
if pkg_name in aiwaf_constraints:
|
|
604
|
+
constraint = aiwaf_constraints[pkg_name]
|
|
605
|
+
max_version = constraint['max_version']
|
|
606
|
+
|
|
607
|
+
try:
|
|
608
|
+
if version.parse(latest_version) > version.parse(max_version):
|
|
609
|
+
# Find the highest safe version
|
|
610
|
+
safe_version = self.find_highest_safe_version(pkg_name, max_version)
|
|
611
|
+
if safe_version and version.parse(safe_version) > version.parse(current_version):
|
|
612
|
+
return {
|
|
613
|
+
'package': pkg,
|
|
614
|
+
'safe': True,
|
|
615
|
+
'target_version': safe_version,
|
|
616
|
+
'reason': f'Upgraded to latest safe version (AIWAF constraint: <={max_version})'
|
|
617
|
+
}
|
|
618
|
+
else:
|
|
619
|
+
return {
|
|
620
|
+
'package': pkg,
|
|
621
|
+
'safe': False,
|
|
622
|
+
'target_version': None,
|
|
623
|
+
'reason': f'{constraint["reason"]} (max safe: {max_version})'
|
|
624
|
+
}
|
|
625
|
+
else:
|
|
626
|
+
# Latest version is within AIWAF constraints
|
|
627
|
+
# Check for other compatibility issues
|
|
628
|
+
compatibility_check = self.check_upgrade_compatibility(pkg, latest_version, all_results)
|
|
629
|
+
if compatibility_check['safe']:
|
|
630
|
+
return {
|
|
631
|
+
'package': pkg,
|
|
632
|
+
'safe': True,
|
|
633
|
+
'target_version': latest_version,
|
|
634
|
+
'reason': 'Safe to upgrade to latest version'
|
|
635
|
+
}
|
|
636
|
+
else:
|
|
637
|
+
return {
|
|
638
|
+
'package': pkg,
|
|
639
|
+
'safe': False,
|
|
640
|
+
'target_version': None,
|
|
641
|
+
'reason': compatibility_check['reason']
|
|
642
|
+
}
|
|
643
|
+
except Exception as e:
|
|
644
|
+
return {
|
|
645
|
+
'package': pkg,
|
|
646
|
+
'safe': False,
|
|
647
|
+
'target_version': None,
|
|
648
|
+
'reason': f'Version parsing error: {e}'
|
|
649
|
+
}
|
|
650
|
+
else:
|
|
651
|
+
# No specific AIWAF constraints, check general compatibility
|
|
652
|
+
compatibility_check = self.check_upgrade_compatibility(pkg, latest_version, all_results)
|
|
653
|
+
return {
|
|
654
|
+
'package': pkg,
|
|
655
|
+
'safe': compatibility_check['safe'],
|
|
656
|
+
'target_version': latest_version if compatibility_check['safe'] else None,
|
|
657
|
+
'reason': compatibility_check['reason'] if not compatibility_check['safe'] else 'No known compatibility issues'
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
def find_highest_safe_version(self, package_name, max_version):
|
|
661
|
+
"""Find the highest version that's still within constraints"""
|
|
662
|
+
try:
|
|
663
|
+
response = requests.get(f'https://pypi.org/pypi/{package_name}/json', timeout=10)
|
|
664
|
+
if response.status_code == 200:
|
|
665
|
+
data = response.json()
|
|
666
|
+
releases = data.get('releases', {})
|
|
667
|
+
|
|
668
|
+
safe_versions = []
|
|
669
|
+
for ver in releases.keys():
|
|
670
|
+
try:
|
|
671
|
+
if version.parse(ver) <= version.parse(max_version):
|
|
672
|
+
safe_versions.append(ver)
|
|
673
|
+
except:
|
|
674
|
+
continue
|
|
675
|
+
|
|
676
|
+
if safe_versions:
|
|
677
|
+
# Sort and return the highest safe version
|
|
678
|
+
safe_versions.sort(key=lambda x: version.parse(x), reverse=True)
|
|
679
|
+
return safe_versions[0]
|
|
680
|
+
except Exception:
|
|
681
|
+
pass
|
|
682
|
+
return None
|
|
683
|
+
|
|
684
|
+
def check_upgrade_compatibility(self, pkg, target_version, all_results):
|
|
685
|
+
"""Check if upgrading a package to target version would cause conflicts"""
|
|
686
|
+
pkg_name = pkg['name'].lower()
|
|
687
|
+
|
|
688
|
+
# Known problematic upgrade scenarios
|
|
689
|
+
if pkg_name == 'numpy' and version.parse(target_version) >= version.parse('2.0.0'):
|
|
690
|
+
# Check if pandas is compatible with NumPy 2.0
|
|
691
|
+
pandas_pkg = next((p for p in all_results if p['name'].lower() == 'pandas'), None)
|
|
692
|
+
if pandas_pkg and pandas_pkg['status'] != 'not_installed':
|
|
693
|
+
pandas_version = pandas_pkg['installed']
|
|
694
|
+
if version.parse(pandas_version) < version.parse('2.1.0'):
|
|
695
|
+
return {
|
|
696
|
+
'safe': False,
|
|
697
|
+
'reason': f'NumPy 2.0+ requires pandas 2.1+, but pandas {pandas_version} is installed'
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
if pkg_name == 'pandas' and version.parse(target_version) >= version.parse('2.0.0'):
|
|
701
|
+
# Check if NumPy is compatible
|
|
702
|
+
numpy_pkg = next((p for p in all_results if p['name'].lower() == 'numpy'), None)
|
|
703
|
+
if numpy_pkg and numpy_pkg['status'] != 'not_installed':
|
|
704
|
+
numpy_version = numpy_pkg['installed']
|
|
705
|
+
if version.parse(numpy_version) < version.parse('1.22.0'):
|
|
706
|
+
return {
|
|
707
|
+
'safe': False,
|
|
708
|
+
'reason': f'pandas 2.0+ requires NumPy 1.22+, but NumPy {numpy_version} is installed'
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
return {'safe': True, 'reason': 'No compatibility issues detected'}
|
|
712
|
+
|
|
713
|
+
def update_requirements_file(self, upgraded_packages):
|
|
714
|
+
"""Update requirements.txt with new package versions"""
|
|
715
|
+
self.stdout.write(self.style.HTTP_INFO("\n📝 Updating requirements.txt..."))
|
|
716
|
+
|
|
717
|
+
requirements_path = os.path.join(settings.BASE_DIR, 'requirements.txt')
|
|
718
|
+
|
|
719
|
+
if not os.path.exists(requirements_path):
|
|
720
|
+
self.stdout.write(self.style.WARNING(f" ⚠️ requirements.txt not found at {requirements_path}"))
|
|
721
|
+
self.stdout.write(" 💡 You can create one manually with updated versions")
|
|
722
|
+
return
|
|
723
|
+
|
|
724
|
+
try:
|
|
725
|
+
# Read current requirements.txt
|
|
726
|
+
with open(requirements_path, 'r') as f:
|
|
727
|
+
lines = f.readlines()
|
|
728
|
+
|
|
729
|
+
# Create backup
|
|
730
|
+
backup_path = requirements_path + '.backup'
|
|
731
|
+
with open(backup_path, 'w') as f:
|
|
732
|
+
f.writelines(lines)
|
|
733
|
+
self.stdout.write(f" 📋 Backup created: {backup_path}")
|
|
734
|
+
|
|
735
|
+
# Update lines with new versions
|
|
736
|
+
updated_lines = []
|
|
737
|
+
updated_count = 0
|
|
738
|
+
|
|
739
|
+
for line in lines:
|
|
740
|
+
original_line = line.strip()
|
|
741
|
+
updated_line = line
|
|
742
|
+
|
|
743
|
+
if original_line and not original_line.startswith('#'):
|
|
744
|
+
# Check if this line contains an upgraded package
|
|
745
|
+
for pkg in upgraded_packages:
|
|
746
|
+
pkg_name = pkg['name']
|
|
747
|
+
new_version = pkg['new_version']
|
|
748
|
+
|
|
749
|
+
# Check various formats: package>=version, package==version, etc.
|
|
750
|
+
if self.line_contains_package(original_line, pkg_name):
|
|
751
|
+
# Update the line with new version
|
|
752
|
+
updated_line = self.update_package_line(original_line, pkg_name, new_version)
|
|
753
|
+
if updated_line != original_line:
|
|
754
|
+
updated_count += 1
|
|
755
|
+
self.stdout.write(f" 📦 {pkg_name}: {original_line} → {updated_line.strip()}")
|
|
756
|
+
break
|
|
757
|
+
|
|
758
|
+
updated_lines.append(updated_line)
|
|
759
|
+
|
|
760
|
+
# Write updated requirements.txt
|
|
761
|
+
with open(requirements_path, 'w') as f:
|
|
762
|
+
f.writelines(updated_lines)
|
|
763
|
+
|
|
764
|
+
if updated_count > 0:
|
|
765
|
+
self.stdout.write(self.style.SUCCESS(f"\n ✅ Updated {updated_count} packages in requirements.txt"))
|
|
766
|
+
self.stdout.write(f" 💾 Original backed up as: {backup_path}")
|
|
767
|
+
else:
|
|
768
|
+
self.stdout.write(self.style.NOTICE(" ℹ️ No package lines found to update in requirements.txt"))
|
|
769
|
+
|
|
770
|
+
except Exception as e:
|
|
771
|
+
self.stdout.write(self.style.ERROR(f" ❌ Error updating requirements.txt: {e}"))
|
|
772
|
+
|
|
773
|
+
def line_contains_package(self, line, package_name):
|
|
774
|
+
"""Check if a requirements line contains the specified package"""
|
|
775
|
+
# Remove comments and whitespace
|
|
776
|
+
line = line.split('#')[0].strip()
|
|
777
|
+
if not line:
|
|
778
|
+
return False
|
|
779
|
+
|
|
780
|
+
# Check if line starts with package name followed by version specifier
|
|
781
|
+
return (line.lower().startswith(package_name.lower()) and
|
|
782
|
+
len(line) > len(package_name) and
|
|
783
|
+
line[len(package_name)] in ['=', '<', '>', '!', '~', ' ', '\t'])
|
|
784
|
+
|
|
785
|
+
def update_package_line(self, line, package_name, new_version):
|
|
786
|
+
"""Update a requirements line with new package version"""
|
|
787
|
+
# Split line into package part and comment part
|
|
788
|
+
parts = line.split('#', 1)
|
|
789
|
+
package_part = parts[0].strip()
|
|
790
|
+
comment_part = f" #{parts[1]}" if len(parts) > 1 else ""
|
|
791
|
+
|
|
792
|
+
# Extract package name and update with new version
|
|
793
|
+
# Use >= to allow for future compatible versions
|
|
794
|
+
updated_package = f"{package_name}>={new_version}"
|
|
795
|
+
|
|
796
|
+
return updated_package + comment_part + '\n'
|
|
797
|
+
|
|
436
798
|
def check_security_vulnerabilities(self, dependencies):
|
|
437
799
|
"""Check for known security vulnerabilities using safety"""
|
|
438
800
|
self.stdout.write(self.style.HTTP_INFO("\n🔒 Checking for security vulnerabilities..."))
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: aiwaf
|
|
3
|
-
Version: 0.1.9.1.
|
|
3
|
+
Version: 0.1.9.1.5
|
|
4
4
|
Summary: AI-powered Web Application Firewall
|
|
5
5
|
Home-page: https://github.com/aayushgauba/aiwaf
|
|
6
6
|
Author: Aayush Gauba
|
|
@@ -123,6 +123,9 @@ aiwaf/
|
|
|
123
123
|
- **Missing dependency** detection
|
|
124
124
|
- **Security vulnerability** scanning
|
|
125
125
|
- **Smart upgrade suggestions** with compatibility validation
|
|
126
|
+
- **Safe automated upgrades** that preserve AIWAF stability
|
|
127
|
+
- **Dry run mode** for testing upgrade plans
|
|
128
|
+
- **Cross-package dependency** analysis and conflict resolution
|
|
126
129
|
|
|
127
130
|
|
|
128
131
|
**Exempt Path & IP Awareness**
|
|
@@ -222,14 +225,51 @@ python manage.py check_dependencies --format json
|
|
|
222
225
|
|
|
223
226
|
# Include security vulnerability scanning
|
|
224
227
|
python manage.py check_dependencies --check-security
|
|
228
|
+
|
|
229
|
+
# Dry run - show what would be upgraded
|
|
230
|
+
python manage.py check_dependencies --upgrade --dry-run
|
|
231
|
+
|
|
232
|
+
# Actually upgrade packages safely
|
|
233
|
+
python manage.py check_dependencies --upgrade
|
|
234
|
+
|
|
235
|
+
# Upgrade packages and update requirements.txt
|
|
236
|
+
python manage.py check_dependencies --upgrade --update-requirements
|
|
237
|
+
|
|
238
|
+
# Full workflow: check, upgrade, and scan for vulnerabilities
|
|
239
|
+
python manage.py check_dependencies --upgrade --check-security --update-requirements
|
|
225
240
|
```
|
|
226
241
|
|
|
227
|
-
**Features:**
|
|
242
|
+
**Core Features:**
|
|
228
243
|
- ✅ **Parses pyproject.toml and requirements.txt**
|
|
229
244
|
- ✅ **Shows current vs latest versions**
|
|
230
245
|
- ✅ **Checks package compatibility** (NumPy 2.0 vs pandas, etc.)
|
|
231
246
|
- ✅ **Detects missing dependencies**
|
|
232
247
|
- ✅ **Security vulnerability scanning** (requires `safety` package)
|
|
248
|
+
- ✅ **Safe package upgrades** (maintains AIWAF stability)
|
|
249
|
+
- ✅ **Dry run mode** for testing upgrade plans
|
|
250
|
+
- ✅ **AIWAF compatibility validation**
|
|
251
|
+
- ✅ **Automatic requirements.txt updates** after successful upgrades
|
|
252
|
+
|
|
253
|
+
**Safe Upgrade System:**
|
|
254
|
+
|
|
255
|
+
The upgrade system is designed to maintain AIWAF stability while keeping your packages up to date:
|
|
256
|
+
|
|
257
|
+
| Protection Level | Description | Example |
|
|
258
|
+
|------------------|-------------|---------|
|
|
259
|
+
| 🛡️ **AIWAF Core** | Never upgrades AIWAF itself | Skips `aiwaf` package |
|
|
260
|
+
| 🔒 **Breaking Changes** | Avoids known problematic versions | Blocks NumPy 2.0+ |
|
|
261
|
+
| 🧠 **Smart Constraints** | Respects AIWAF compatibility matrix | pandas ≤ 2.9.99 |
|
|
262
|
+
| 🔍 **Dependency Analysis** | Checks cross-package compatibility | NumPy vs pandas versions |
|
|
263
|
+
|
|
264
|
+
**AIWAF Compatibility Matrix:**
|
|
265
|
+
|
|
266
|
+
| Package | Safe Range | Blocked Versions | Reason |
|
|
267
|
+
|---------|------------|------------------|---------|
|
|
268
|
+
| Django | 3.2+ | None | AIWAF compatible with all Django versions |
|
|
269
|
+
| NumPy | 1.21 - 1.99 | 2.0+ | Avoid breaking changes |
|
|
270
|
+
| pandas | 1.3 - 2.9 | 3.0+ | AIWAF compatibility |
|
|
271
|
+
| scikit-learn | 1.0 - 1.99 | 2.0+ | Model compatibility |
|
|
272
|
+
| joblib | 1.1 - 1.99 | 2.0+ | AIWAF tested range |
|
|
233
273
|
- ✅ **Provides upgrade commands**
|
|
234
274
|
|
|
235
275
|
**Example Output:**
|
|
@@ -249,10 +289,79 @@ python manage.py check_dependencies --check-security
|
|
|
249
289
|
🔍 Checking package compatibility...
|
|
250
290
|
✅ All packages appear to be compatible!
|
|
251
291
|
|
|
252
|
-
|
|
253
|
-
|
|
292
|
+
� Planning safe package upgrades...
|
|
293
|
+
|
|
294
|
+
✅ SAFE UPGRADES PLANNED:
|
|
295
|
+
────────────────────────────────────────
|
|
296
|
+
📦 pandas 1.3.5 → 1.5.3 (Latest: 2.2.2)
|
|
297
|
+
💡 Upgraded to latest safe version (AIWAF constraint: <=1.99.99)
|
|
298
|
+
📦 joblib 1.1.0 → 1.4.2 (Latest: 1.4.2)
|
|
299
|
+
💡 Safe to upgrade to latest version
|
|
300
|
+
|
|
301
|
+
⚠️ UPGRADES BLOCKED FOR STABILITY:
|
|
302
|
+
────────────────────────────────────────
|
|
303
|
+
❌ numpy 1.21.0 ✗ 2.0.1
|
|
304
|
+
🚨 NumPy 2.0+ may cause compatibility issues (max safe: 1.99.99)
|
|
305
|
+
|
|
306
|
+
🎉 Upgrade complete: 2/2 packages upgraded successfully
|
|
307
|
+
|
|
308
|
+
📝 Updating requirements.txt...
|
|
309
|
+
📋 Backup created: requirements.txt.backup
|
|
310
|
+
📦 pandas: pandas>=1.3 → pandas>=1.5.3
|
|
311
|
+
📦 joblib: joblib>=1.1 → joblib>=1.4.2
|
|
312
|
+
✅ Updated 2 packages in requirements.txt
|
|
313
|
+
💾 Original backed up as: requirements.txt.backup
|
|
314
|
+
|
|
315
|
+
�💡 To update outdated packages, run:
|
|
316
|
+
pip install --upgrade pandas==1.5.3 joblib
|
|
254
317
|
```
|
|
255
318
|
|
|
319
|
+
**Safe Upgrade System:**
|
|
320
|
+
- 🛡️ **AIWAF Protection**: Never breaks AIWAF functionality
|
|
321
|
+
- 🔍 **Compatibility Validation**: Checks package interdependencies
|
|
322
|
+
- 📊 **Conservative Constraints**: Avoids known problematic versions
|
|
323
|
+
- 🧪 **Dry Run Mode**: Test upgrade plans before execution
|
|
324
|
+
- ⚠️ **Clear Blocking Reasons**: Explains why upgrades are blocked
|
|
325
|
+
- 📝 **Requirements.txt Updates**: Automatically updates dependency files
|
|
326
|
+
|
|
327
|
+
**Recommended Upgrade Workflow:**
|
|
328
|
+
|
|
329
|
+
1. **Check current status:**
|
|
330
|
+
```bash
|
|
331
|
+
python manage.py check_dependencies
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
2. **Preview safe upgrades:**
|
|
335
|
+
```bash
|
|
336
|
+
python manage.py check_dependencies --upgrade --dry-run
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
3. **Execute safe upgrades:**
|
|
340
|
+
```bash
|
|
341
|
+
python manage.py check_dependencies --upgrade --update-requirements
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
4. **Verify after upgrade:**
|
|
345
|
+
```bash
|
|
346
|
+
python manage.py check_dependencies
|
|
347
|
+
python manage.py detect_and_train # Retrain with new packages
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
5. **Test your application:**
|
|
351
|
+
```bash
|
|
352
|
+
python manage.py test # Run your test suite
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
**Upgrade Decision Logic:**
|
|
356
|
+
|
|
357
|
+
The system uses a multi-layer decision process:
|
|
358
|
+
|
|
359
|
+
- **Layer 1**: Skip AIWAF itself (manual upgrade recommended)
|
|
360
|
+
- **Layer 2**: Check AIWAF compatibility constraints
|
|
361
|
+
- **Layer 3**: Analyze cross-package dependencies
|
|
362
|
+
- **Layer 4**: Select highest safe version within constraints
|
|
363
|
+
- **Layer 5**: Execute with error handling and rollback capability
|
|
364
|
+
|
|
256
365
|
This will ensure the IP is never blocked by AI‑WAF. You can also manage exemptions via the Django admin interface.
|
|
257
366
|
|
|
258
367
|
- **Daily Retraining**
|
|
@@ -444,6 +553,68 @@ MIDDLEWARE = [
|
|
|
444
553
|
- Missing migrations: `python manage.py migrate`
|
|
445
554
|
- Import errors: Check `INSTALLED_APPS` includes `'aiwaf'`
|
|
446
555
|
|
|
556
|
+
### **Dependency Upgrade Troubleshooting**
|
|
557
|
+
|
|
558
|
+
**Common Upgrade Scenarios:**
|
|
559
|
+
|
|
560
|
+
1. **NumPy 2.0 Upgrade Blocked:**
|
|
561
|
+
```bash
|
|
562
|
+
# Check pandas compatibility first
|
|
563
|
+
python manage.py check_dependencies --upgrade --dry-run
|
|
564
|
+
|
|
565
|
+
# If pandas < 2.1, upgrade pandas first
|
|
566
|
+
pip install 'pandas>=2.1,<3.0'
|
|
567
|
+
|
|
568
|
+
# Then allow NumPy upgrade
|
|
569
|
+
python manage.py check_dependencies --upgrade
|
|
570
|
+
```
|
|
571
|
+
|
|
572
|
+
2. **All Upgrades Blocked:**
|
|
573
|
+
```bash
|
|
574
|
+
# Check what's blocking upgrades
|
|
575
|
+
python manage.py check_dependencies --upgrade --dry-run
|
|
576
|
+
|
|
577
|
+
# Manual override (use with caution)
|
|
578
|
+
pip install --upgrade package-name
|
|
579
|
+
|
|
580
|
+
# Verify AIWAF still works
|
|
581
|
+
python manage.py detect_and_train
|
|
582
|
+
```
|
|
583
|
+
|
|
584
|
+
3. **Package Conflict After Upgrade:**
|
|
585
|
+
```bash
|
|
586
|
+
# Check current compatibility
|
|
587
|
+
python manage.py check_dependencies
|
|
588
|
+
|
|
589
|
+
# Downgrade to last known good version
|
|
590
|
+
pip install package-name==previous-version
|
|
591
|
+
|
|
592
|
+
# Find safe upgrade path
|
|
593
|
+
python manage.py check_dependencies --upgrade --dry-run
|
|
594
|
+
```
|
|
595
|
+
|
|
596
|
+
4. **AIWAF Model Issues After Upgrade:**
|
|
597
|
+
```bash
|
|
598
|
+
# Regenerate model with new package versions
|
|
599
|
+
python manage.py regenerate_model
|
|
600
|
+
|
|
601
|
+
# Retrain with current environment
|
|
602
|
+
python manage.py detect_and_train
|
|
603
|
+
```
|
|
604
|
+
|
|
605
|
+
**Emergency Rollback:**
|
|
606
|
+
If an upgrade breaks your system:
|
|
607
|
+
```bash
|
|
608
|
+
# Reinstall exact previous versions
|
|
609
|
+
pip install package-name==old-version
|
|
610
|
+
|
|
611
|
+
# Or use requirements.txt backup
|
|
612
|
+
pip install -r requirements.txt.backup
|
|
613
|
+
|
|
614
|
+
# Verify AIWAF functionality
|
|
615
|
+
python manage.py aiwaf_diagnose
|
|
616
|
+
```
|
|
617
|
+
|
|
447
618
|
---
|
|
448
619
|
|
|
449
620
|
## Running Detection & Training
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
aiwaf/__init__.py,sha256=
|
|
1
|
+
aiwaf/__init__.py,sha256=XpqePdxlyW-5RJkAUTNtlGeSUYFl7vaRQ6JrnHU1W-Y,220
|
|
2
2
|
aiwaf/apps.py,sha256=nCez-Ptlv2kaEk5HenA8b1pATz1VfhrHP1344gwcY1A,142
|
|
3
3
|
aiwaf/blacklist_manager.py,sha256=LYCeKFB-7e_C6Bg2WeFJWFIIQlrfRMPuGp30ivrnhQY,1196
|
|
4
4
|
aiwaf/decorators.py,sha256=IUKOdM_gdroffImRZep1g1wT6gNqD10zGwcp28hsJCs,825
|
|
@@ -15,7 +15,7 @@ aiwaf/management/commands/add_ipexemption.py,sha256=sSf3d9hGK9RqqlBYkCrnrd8KZWGT
|
|
|
15
15
|
aiwaf/management/commands/aiwaf_diagnose.py,sha256=nXFRhq66N4QC3e4scYJ2sUngJce-0yDxtBO3R2BllRM,6134
|
|
16
16
|
aiwaf/management/commands/aiwaf_logging.py,sha256=FCIqULn2tii2vD9VxL7vk3PV4k4vr7kaA00KyaCExYY,7692
|
|
17
17
|
aiwaf/management/commands/aiwaf_reset.py,sha256=0FIBqpZS8xgFFvAKJ-0zAC_-QNQwRkOHpXb8N-OdFr8,3740
|
|
18
|
-
aiwaf/management/commands/check_dependencies.py,sha256=
|
|
18
|
+
aiwaf/management/commands/check_dependencies.py,sha256=pxON6cFrBTWtNRzPOzB_S4Jdld1G6q1O_ds3xtbjq8Y,36134
|
|
19
19
|
aiwaf/management/commands/clear_cache.py,sha256=cdnuTgxkhKLqT_6k6yTcEBlREovNRQxAE51ceXlGYMA,647
|
|
20
20
|
aiwaf/management/commands/debug_csv.py,sha256=Lddqp37mIn0zdvHf4GbuNTWYyJ5h8bumDcGmFSAioi0,6801
|
|
21
21
|
aiwaf/management/commands/detect_and_train.py,sha256=-o-LZ7QZ5GeJPCekryox1DGXKMmFEkwwrcDsiM166K0,269
|
|
@@ -27,8 +27,8 @@ aiwaf/management/commands/test_exemption_fix.py,sha256=ngyGaHUCmQQ6y--6j4q1viZJt
|
|
|
27
27
|
aiwaf/resources/model.pkl,sha256=5t6h9BX8yoh2xct85MXOO60jdlWyg1APskUOW0jZE1Y,1288265
|
|
28
28
|
aiwaf/templatetags/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
29
29
|
aiwaf/templatetags/aiwaf_tags.py,sha256=XXfb7Tl4DjU3Sc40GbqdaqOEtKTUKELBEk58u83wBNw,357
|
|
30
|
-
aiwaf-0.1.9.1.
|
|
31
|
-
aiwaf-0.1.9.1.
|
|
32
|
-
aiwaf-0.1.9.1.
|
|
33
|
-
aiwaf-0.1.9.1.
|
|
34
|
-
aiwaf-0.1.9.1.
|
|
30
|
+
aiwaf-0.1.9.1.5.dist-info/licenses/LICENSE,sha256=Ir8PX4dxgAcdB0wqNPIkw84fzIIRKE75NoUil9RX0QU,1069
|
|
31
|
+
aiwaf-0.1.9.1.5.dist-info/METADATA,sha256=7hXUI5PEFAGOTugMBbECEz1SgMHbb8NEnYt_Alg4yFM,21873
|
|
32
|
+
aiwaf-0.1.9.1.5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
33
|
+
aiwaf-0.1.9.1.5.dist-info/top_level.txt,sha256=kU6EyjobT6UPCxuWpI_BvcHDG0I2tMgKaPlWzVxe2xI,6
|
|
34
|
+
aiwaf-0.1.9.1.5.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|