Coverage for product_risk_suite/threat_model/views.py: 92%

61 statements  

« prev     ^ index     » next       coverage.py v7.15.0, created at 2026-07-07 12:45 +0000

1from django.shortcuts import get_object_or_404, render 

2from django.views.decorators.csrf import csrf_protect 

3from django.contrib.auth.decorators import login_required 

4from django.core.exceptions import PermissionDenied 

5from django.http import FileResponse 

6 

7import json 

8from guardian.shortcuts import get_objects_for_user 

9 

10from product.models import ProductRiskEntry, ProductRiskAnalysis 

11from .models import ThreatModel 

12 

13@login_required 

14def serve_threat_model_svg(request, filename): 

15 tm = get_object_or_404(ThreatModel, svg=f'uploads/{filename}') 

16 if not request.user.has_perm('product.view_product', tm.product): 

17 raise PermissionDenied 

18 return FileResponse(tm.svg.open('rb'), content_type='image/svg+xml') 

19 

20 

21@csrf_protect 

22@login_required 

23def threat_model_index(request): 

24 products = get_objects_for_user(request.user, 'product.view_product') 

25 tms = ThreatModel.objects.filter(product__in=products).order_by("product").order_by("title") 

26 

27 context = {"tm_list": tms} 

28 return render(request, "threat_model_index.html", context) 

29 

30@csrf_protect 

31@login_required 

32def threat_model(request, threat_model_slug): 

33 products = get_objects_for_user(request.user, 'product.view_product') 

34 tm = get_object_or_404(ThreatModel.objects.filter(product__in=products), slug=threat_model_slug) 

35 svg_content = tm.get_clean_svg_content() 

36 

37 svg_ids = tm.connection_names.all() 

38 risk_entries = ProductRiskEntry.objects.filter(svg_id__in=svg_ids).distinct().order_by("risk__custom_id") 

39 json_entries = {} 

40 names = {} 

41 for entry in risk_entries: 

42 for s in entry.svg_id.all(): 

43 if not s.tech_name in json_entries: 

44 json_entries[s.tech_name] = [] 

45 

46 stride_ul = "<ul>" 

47 for stride in entry.risk.list_stride: 

48 stride_ul = f"{stride_ul}<li>{stride}</li>" 

49 stride_ul = f"{stride_ul}</ul>" 

50 

51 product_id = tm.product.slug 

52 # always link to the last analysis found 

53 analysis_id = None 

54 try: 

55 last_analysis = ProductRiskAnalysis.objects.filter(risk_entries=entry).latest('id') 

56 analysis_id = last_analysis.slug 

57 except (ProductRiskEntry.DoesNotExist, AttributeError): 

58 pass 

59 except (ProductRiskAnalysis.DoesNotExist, AttributeError): 

60 pass 

61 except (Product.DoesNotExist, AttributeError): 

62 pass 

63 

64 mit = entry.risk.suggested_mitigation_validation.suggested_mitigation if entry.risk.suggested_mitigation_validation else "" 

65 val = entry.risk.suggested_mitigation_validation.suggested_validation if entry.risk.suggested_mitigation_validation else "" 

66 analysis_link = entry.risk.custom_id 

67 if analysis_id: 

68 analysis_link = f"""<a target="_blank" rel="noopener noreferrer" href="/product/{product_id}/{analysis_id}/#{entry.risk.custom_id}">{entry.risk.custom_id}</a>""" 

69 tr = f"""<tr> 

70 <td>{analysis_link}</td> 

71 <td>{entry.risk.asset.name}</td> 

72 <td>{stride_ul}</td> 

73 <td>{entry.risk.title}</td> 

74 <td>{entry.risk.description}</td> 

75 <td>{mit}</td> 

76 <td>{val}</td> 

77 </tr>""" 

78 json_entries[s.tech_name].append(tr) 

79 names[s.tech_name] = str(s) 

80 

81 context = {"tm": tm, "svg_content": svg_content, "risk_entries": json.dumps(json_entries), "names": names} 

82 return render(request, "threat_model.html", context)