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
« 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
7import json
8from guardian.shortcuts import get_objects_for_user
10from product.models import ProductRiskEntry, ProductRiskAnalysis
11from .models import ThreatModel
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')
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")
27 context = {"tm_list": tms}
28 return render(request, "threat_model_index.html", context)
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()
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] = []
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>"
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
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)
81 context = {"tm": tm, "svg_content": svg_content, "risk_entries": json.dumps(json_entries), "names": names}
82 return render(request, "threat_model.html", context)