Repository: OWASP/owasp-mastg

Issue:
Fix Checklists Coverage for MSTG Test Cases
### Discussed in https://github.com/OWASP/owasp-mstg/discussions/2082

<div type='discussions-op-text'>

<sup>Originally posted by **fujiokayu** March  6, 2022</sup>
since [V1.4.0](https://github.com/OWASP/owasp-mstg/releases/tag/v1.4.0), the OWASP Mobile App Security Checklists have been renewed and changed to be automatically generated.
thanks for the great works.

However, the Test Case links are fewer and noticeably blank compared to past versions.
Those already familiar with this project will have no problem.
But those who are trying to use checklist for the first time, or clients who are given a checklist as evidence of testing, may be a bit confused.

One major reason is that the Python program does not target the "0x04" document.
Fixing this will fill in most of the holes in V4 and V5, and a few in V2, V3, and V6.
if you would like to include this fix, I will send a PR soon.

- tools/scripts/mstg_to_html.sh

    ```diff
    - for filename in Document/0x05*.md Document/0x06*.md; do
    + for filename in Document/0x04*.md Document/0x05*.md Document/0x06*.md; do
    ```

- tools/scripts/yaml_to_excel.py
    ```diff
            # We only get the first link because there should be actually only one per platform.
            link_android = get_link_for(req["links"], "0x05")
            link_ios = get_link_for(req["links"], "0x06")
    +       link_common = get_link_for(req["links"], "0x04")

            if link_android:
                ws.cell(row=row, column=col_link_android).value = f'=HYPERLINK("{link_android}", "Test Case")'
                ws.cell(row=row, column=col_link_ios).value = "N/A"
                ws.cell(row=row, column=col_link_ios).style = "gray_header"

    +        # If a cell is null or "N/A", and a 0x04 link exists, write it in the test case.
    +        if link_common:
    +            if ws.cell(row=row, column=col_link_android).value is None or ws.cell(row=row, column=col_link_android).value == "N/A": 
    +                ws.cell(row=row, column=col_link_android).value = f'=HYPERLINK("{link_common}", "Test Case")'
    +                ws.cell(row=row, column=col_link_android).style = "Hyperlink"
    +                ws.cell(row=row, column=col_link_android).alignment = excel_styles_and_validation.align_center
    +            if ws.cell(row=row, column=col_link_ios).value is None or ws.cell(row=row, column=col_link_ios).value == "N/A": 
    +                ws.cell(row=row, column=col_link_ios).value = f'=HYPERLINK("{link_common}", "Test Case")'
    +                ws.cell(row=row, column=col_link_ios).style = "Hyperlink"
    +                ws.cell(row=row, column=col_link_ios).alignment = excel_styles_and_validation.align_center
    ```

As a sample, share the checklist that generated with this modification applied.
https://drive.google.com/drive/folders/1FI8VHUO_MJVfcwbRFUffZAI7zlxbFpGD?usp=sharing

But this is not a perfect fix, so I'd be happy to hear everyone's opinions.</div>


Discussion:
Hi @fujiokayu, here's the issue, could you please open a PR including your changes? Please write `Closes #2084` in the description so that it is linked to this issue.

Once it is created we can take a look at the generated files and see it it need some more fixes.

I've sent you an invitation to join our project on GitHub, once you accept it I'll assign this issue to you.

Thank you very much again, this is very helpful!
Thanks @cpholguera for the invitation, I accepted.
I will clean up the code a bit and send you a PR within a few days.
どうもありがとうございます、 @fujiokayu! 🙏

Resolving patch:
diff --git a/tools/scripts/yaml_to_excel.py b/tools/scripts/yaml_to_excel.py
--- a/tools/scripts/yaml_to_excel.py
+++ b/tools/scripts/yaml_to_excel.py
@@ -51,6 +51,7 @@
 MSTGCOMMIT = ""
 MASVSVERSION = ""
 MASVSCOMMIT = ""
+TEST_CASE_ALIAS = "Test Case"
 
 WS_BASE_CONFIG = {
     "start_row": 6,
@@ -114,6 +115,11 @@ def write_title(ws, row, start_column, end_column, title):
 
     ws.row_dimensions[row].height = 25  # points
 
+def write_testcase(ws, row, column, url_string):
+    ws.cell(row=row, column=column).value = f'=HYPERLINK("{url_string}", "{TEST_CASE_ALIAS}")'
+    ws.cell(row=row, column=column).style = "Hyperlink"
+    ws.cell(row=row, column=column).alignment = excel_styles_and_validation.align_center
+
 def get_link_for(links, type):
     for link in links:
         if type in link:
@@ -184,26 +190,32 @@ def create_security_requirements_sheet(wb):
         
         if req.get("links"):
             # We only get the first link because there should be actually only one per platform.
+            link_common = get_link_for(req["links"], "0x04")
             link_android = get_link_for(req["links"], "0x05")
             link_ios = get_link_for(req["links"], "0x06")
 
             if link_android:
-                ws.cell(row=row, column=col_link_android).value = f'=HYPERLINK("{link_android}", "Test Case")'
-                ws.cell(row=row, column=col_link_android).style = "Hyperlink"
-                ws.cell(row=row, column=col_link_android).alignment = excel_styles_and_validation.align_center
+                write_testcase(ws, row, col_link_android, link_android)
             else:
                 ws.cell(row=row, column=col_link_android).value = "N/A"
                 ws.cell(row=row, column=col_link_android).style = "gray_header"
             
             if link_ios:
-                ws.cell(row=row, column=col_link_ios).value = f'=HYPERLINK("{link_ios}", "Test Case")'
-                ws.cell(row=row, column=col_link_ios).style = "Hyperlink"
-                ws.cell(row=row, column=col_link_ios).alignment = excel_styles_and_validation.align_center
+                write_testcase(ws, row, col_link_ios, link_ios)
 
             else:
                 ws.cell(row=row, column=col_link_ios).value = "N/A"
                 ws.cell(row=row, column=col_link_ios).style = "gray_header"
 
+            # If 0x04 link exists, and a cell is None or "N/A", then write the link as common test case.
+            if link_common:
+                if  ws.cell(row=row, column=col_link_android).value is None \
+                    or ws.cell(row=row, column=col_link_android).value == "N/A":
+                    write_testcase(ws, row, col_link_android, link_common)
+                if  ws.cell(row=row, column=col_link_ios).value is None \
+                    or ws.cell(row=row, column=col_link_ios).value == "N/A":
+                    write_testcase(ws, row, col_link_ios, link_common)
+
         ws.row_dimensions[row].height = 55  # points
         
         status_cell = ws.cell(row=row, column=col_status).coordinate


Test patch: