Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions application/tests/cheatsheet_extractor_test.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import subprocess
import tempfile
import unittest
from pathlib import Path

from application.utils.external_project_parsers.parsers.cheatsheet_extractor import (
extract_cheatsheet_record,
_get_committed_at,
)
from application.defs.cheatsheet_defs import SUMMARY_MAX_LENGTH

Expand Down Expand Up @@ -165,5 +170,68 @@ def test_fallback_not_used(self):
self.assertEqual(self.record.metadata["fallback_used"], "false")


def _run_git(args, cwd):
subprocess.run(["git", *args], cwd=cwd, check=True, capture_output=True)


## Happy path
class TestGetCommittedAt(unittest.TestCase):
def test_returns_iso_timestamp_for_tracked_file(self):
with tempfile.TemporaryDirectory() as repo_dir:
_run_git(["init", "-q"], cwd=repo_dir)
_run_git(["config", "user.email", "test@test.com"], cwd=repo_dir)
_run_git(["config", "user.name", "test"], cwd=repo_dir)

file_path = Path(repo_dir) / "Some_Cheat_Sheet.md"
file_path.write_text("# Some Cheat Sheet\n")

_run_git(["add", "."], cwd=repo_dir)
_run_git(["commit", "-q", "-m", "add cheat sheet"], cwd=repo_dir)

result = _get_committed_at(str(file_path))

self.assertRegex(
result,
r"^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:[+-]\d{2}:\d{2}|Z)$",
)

## Tests file when cwd is not git based
def test_returns_fallback_when_not_in_a_git_repo(self):
with tempfile.TemporaryDirectory() as plain_dir:
file_path = Path(plain_dir) / "No_Repo_Cheat_Sheet.md"
file_path.write_text("# No repo\n")

result = _get_committed_at(str(file_path))

self.assertEqual(result, "")

## Tests file when cwd is elsewhere
def test_finds_timestamp_even_when_process_cwd_is_elsewhere(self):
import os

with tempfile.TemporaryDirectory() as repo_dir, tempfile.TemporaryDirectory() as unrelated_dir:
_run_git(["init", "-q"], cwd=repo_dir)
_run_git(["config", "user.email", "test@test.com"], cwd=repo_dir)
_run_git(["config", "user.name", "test"], cwd=repo_dir)

file_path = Path(repo_dir) / "Some_Cheat_Sheet.md"
file_path.write_text("# Some Cheat Sheet\n")

_run_git(["add", "."], cwd=repo_dir)
_run_git(["commit", "-q", "-m", "add cheat sheet"], cwd=repo_dir)

original_cwd = os.getcwd()
try:
os.chdir(unrelated_dir) # simulate script launched elsewhere
result = _get_committed_at(str(file_path))
finally:
os.chdir(original_cwd)

self.assertRegex(
result,
r"^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:[+-]\d{2}:\d{2}|Z)$",
)


if __name__ == "__main__":
unittest.main()
92 changes: 92 additions & 0 deletions application/tests/cheatsheet_record_adapter_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import unittest

from application.defs.cheatsheet_defs import CheatsheetRecord
from application.utils.external_project_parsers.parsers.cheatsheet_record_adapter import (
MalformedCheatsheetRecordError,
section_from_cheatsheet_record,
)


class TestSectionFromCheatsheetRecord(unittest.TestCase):
def test_valid_record_produces_expected_section(self):
record = CheatsheetRecord(
source_id="Secrets_Management_Cheat_Sheet",
title="Secrets Management Cheat Sheet",
hyperlink="https://cheatsheetseries.owasp.org/cheatsheets/Secrets_Management_Cheat_Sheet.html",
summary="Storage guidance.",
headings=["Introduction", "Architectural Patterns"],
raw_markdown_path="cheatsheets/Secrets_Management_Cheat_Sheet.md",
metadata={
"parser_version": "v1",
"fallback_used": "false",
"committed_at": "2026-06-14T10:22:03+00:00",
},
)

section = section_from_cheatsheet_record(record)

self.assertEqual(
section.chunk_id, "chk:owasp_cheatsheets:Secrets_Management_Cheat_Sheet"
)
self.assertEqual(
section.artifact_id, "art:owasp_cheatsheets:Secrets_Management_Cheat_Sheet"
)
self.assertEqual(
section.text, "Storage guidance.\nIntroduction\nArchitectural Patterns"
)
self.assertEqual(section.title_hint, "Secrets Management Cheat Sheet")
self.assertEqual(section.language, "en")
self.assertEqual(
str(section.source.url),
"https://cheatsheetseries.owasp.org/cheatsheets/Secrets_Management_Cheat_Sheet.html",
)
self.assertEqual(
str(section.source.committed_at.isoformat()), "2026-06-14T10:22:03+00:00"
)
self.assertEqual(section.locator.id, "Secrets_Management_Cheat_Sheet")
self.assertEqual(
str(section.locator.url),
"https://cheatsheetseries.owasp.org/cheatsheets/Secrets_Management_Cheat_Sheet.html",
)

def test_fallback_title_and_summary_pass_through(self):
record = CheatsheetRecord(
source_id="Secrets_Management_Cheat_Sheet",
title="No title found.",
hyperlink="https://cheatsheetseries.owasp.org/cheatsheets/Secrets_Management_Cheat_Sheet.html",
summary="No summary found.",
headings=[],
raw_markdown_path="cheatsheets/Secrets_Management_Cheat_Sheet.md",
metadata={
"parser_version": "v1",
"fallback_used": "true",
"committed_at": "2026-06-14T10:22:03+00:00",
},
)

section = section_from_cheatsheet_record(record)

self.assertEqual(section.title_hint, "No title found.")
self.assertEqual(section.text, "No summary found.")

def test_missing_committed_at_raises(self):
record = CheatsheetRecord(
source_id="Secrets_Management_Cheat_Sheet",
title="Secrets Management Cheat Sheet",
hyperlink="https://cheatsheetseries.owasp.org/cheatsheets/Secrets_Management_Cheat_Sheet.html",
summary="Storage guidance.",
headings=["Introduction"],
raw_markdown_path="cheatsheets/Secrets_Management_Cheat_Sheet.md",
metadata={
"parser_version": "v1",
"fallback_used": "false",
"committed_at": "",
},
)

with self.assertRaises(MalformedCheatsheetRecordError):
section_from_cheatsheet_record(record)


if __name__ == "__main__":
unittest.main()
64 changes: 64 additions & 0 deletions application/tests/librarian/fixtures/golden_dataset.json
Original file line number Diff line number Diff line change
Expand Up @@ -6369,5 +6369,69 @@
"provenance": {
"ground_truth_source": "manually synthesised broad statement that should route to human review (no single clear CRE target)"
}
},
{
"id": "gold:cheatsheet:authorization:positive",
"schema_version": "0.1.0",
"slice": "positive",
"input": {
"text": "Authorization may be defined as \"the process of verifying that a requested action or service is approved for a specific entity\" ([NIST](https://csrc.nist.gov/glossary/term/authorization)). Authorization is distinct from authentication which is the process of verifying an entity's identity. When designing and developing a software solution, it is important to keep these distinctions in mind. A user who has been authenticated (perhaps by providing a username and password) is often not authorized t\nIntroduction\nRecommendations\nReferences",
"title_hint": "Authorization Cheat Sheet",
"source_standard": "OWASP_CHEATSHEET"
},
"expected": {
"decision": "linked",
"cre_ids": [
"128-128",
"117-371"
]
},
"provenance": {
"section_path": "Authorization_Cheat_Sheet",
"ground_truth_source": "owasp_cheatsheets_supplement.json"
}
},
{
"id": "gold:cheatsheet:rest-security:positive",
"schema_version": "0.1.0",
"slice": "positive",
"input": {
"text": "[REST](https://en.wikipedia.org/wiki/REST) (or **RE**presentational **S**tate **T**ransfer) is an architectural style first described in [Roy Fielding](https://en.wikipedia.org/wiki/Roy_Fielding)'s Ph.D. dissertation on [Architectural Styles and the Design of Network-based Software Architectures](https://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm).\n\nIt evolved as Fielding wrote the HTTP/1.1 and URI specs and has been proven to be well-suited for developing distributed hypermedia applica\nIntroduction\nHTTPS\nAccess Control\nJWT\nAPI Keys\nRestrict HTTP methods\nPreventing Out-of-Order API Execution\nInput validation\nValidate content types\nManagement endpoints\nError handling\nAudit logs\nSecurity Headers\nCORS\nSensitive information in HTTP requests\nHTTP Return Code",
"title_hint": "REST Security Cheat Sheet",
"source_standard": "OWASP_CHEATSHEET"
},
"expected": {
"decision": "linked",
"cre_ids": [
"118-110",
"724-770",
"623-550"
]
},
"provenance": {
"section_path": "REST_Security_Cheat_Sheet",
"ground_truth_source": "owasp_cheatsheets_supplement.json"
}
},
{
"id": "gold:cheatsheet:ssrf-prevention:positive",
"schema_version": "0.1.0",
"slice": "positive",
"input": {
"text": "The objective of the cheat sheet is to provide advice regarding the protection against [Server Side Request Forgery](https://www.acunetix.com/blog/articles/server-side-request-forgery-vulnerability/) (SSRF) attack.\n\nThis cheat sheet will focus on the defensive point of view and will not explain how to perform this attack. This [talk](../assets/Server_Side_Request_Forgery_Prevention_Cheat_Sheet_Orange_Tsai_Talk.pdf) from the security researcher [Orange Tsai](https://twitter.com/orange_8361) as we\nIntroduction\nContext\nOverview of a SSRF common flow\nCases\nIMDSv2 in AWS\nDeny-list (Last Resort)\nSemgrep Rules\nReferences\nTools and code used for schemas",
"title_hint": "Server-Side Request Forgery Prevention Cheat Sheet",
"source_standard": "OWASP_CHEATSHEET"
},
"expected": {
"decision": "linked",
"cre_ids": [
"028-728",
"657-084"
]
},
"provenance": {
"section_path": "Server_Side_Request_Forgery_Prevention_Cheat_Sheet",
"ground_truth_source": "owasp_cheatsheets_supplement.json"
}
}
]
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import logging
import os
import re
import subprocess

from application.defs.cheatsheet_defs import CheatsheetRecord

Expand Down Expand Up @@ -92,6 +93,30 @@ def _fallback_summary(markdown: str) -> str:
return "No summary found."


def _get_committed_at(source_path: str) -> str:
"""Return the ISO 8601 last-commit timestamp for source_path, or '' if unavailable."""

repo_dir = os.path.dirname(os.path.abspath(source_path)) or "."

try:
result = subprocess.run(
["git", "log", "-1", "--format=%cI", "--", os.path.basename(source_path)],
cwd=repo_dir,
capture_output=True,
text=True,
check=True,
)
Comment thread
coderabbitai[bot] marked this conversation as resolved.
except (subprocess.CalledProcessError, FileNotFoundError, OSError) as e:
logging.warning(
"_get_committed_at: commit not found for %s: %s",
source_path,
e,
)
return ""

return result.stdout.strip()
Comment thread
Abhijeet2409 marked this conversation as resolved.


def extract_cheatsheet_record(
markdown: str,
source_path: str,
Expand Down Expand Up @@ -119,6 +144,7 @@ def extract_cheatsheet_record(

source_id = _derive_source_id(source_path)
hyperlink = _derive_hyperlink(source_path)
committed_at = _get_committed_at(source_path)

return CheatsheetRecord(
source_id=source_id,
Expand All @@ -131,5 +157,6 @@ def extract_cheatsheet_record(
metadata={
"parser_version": PARSER_VERSION,
"fallback_used": fallback_used,
"committed_at": committed_at,
},
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
from pydantic import ValidationError
from application.utils.librarian.section_validator import _DEFAULT_LANGUAGE
from application.defs.cheatsheet_defs import CheatsheetRecord
from application.utils.librarian.schemas import (
Locator,
LocatorKind,
SourceRef,
SourceType,
)
from application.utils.librarian.section_validator import (
Section,
SectionValidationError,
)


class MalformedCheatsheetRecordError(SectionValidationError):
pass


def section_from_cheatsheet_record(
record: CheatsheetRecord,
) -> Section:
text = "\n".join([record.summary, *record.headings])

try:
source = SourceRef(
type=SourceType.url,
url=record.hyperlink,
committed_at=record.metadata.get("committed_at"),
)

locator = Locator(
kind=LocatorKind.url,
id=record.source_id,
url=record.hyperlink,
)
except ValidationError as exc:
raise MalformedCheatsheetRecordError(str(exc)) from exc

return Section(
artifact_id=f"art:{record.source}:{record.source_id}",
chunk_id=f"chk:{record.source}:{record.source_id}",
Comment thread
Abhijeet2409 marked this conversation as resolved.
text=text,
title_hint=record.title,
language=_DEFAULT_LANGUAGE,
source=source,
locator=locator,
)
Loading
Loading