Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove email #325

Merged
merged 6 commits into from
Oct 30, 2024
Merged
Show file tree
Hide file tree
Changes from 3 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
1 change: 0 additions & 1 deletion compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ services:
MICROSOFT_TENANT_ID: tenant_id
MICROSOFT_CLIENT_ID: client_id
MICROSOFT_CLIENT_SECRET: client_secret
EMAIL_RECIPIENT: email_recipient
DRAGONFLY_GITHUB_TOKEN: test
volumes:
- "./src:/app/src"
Expand Down
60 changes: 20 additions & 40 deletions src/mainframe/endpoints/report.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
from mainframe.json_web_token import AuthenticationData
from mainframe.models.orm import Scan
from mainframe.models.schemas import (
EmailReport,
Error,
ObservationKind,
ObservationReport,
Expand Down Expand Up @@ -121,20 +120,17 @@ def _validate_additional_information(body: ReportPackageBody, scan: Scan):
log = logger.bind(package={"name": body.name, "version": body.version})

if body.additional_information is None:
if len(scan.rules) == 0 or body.use_email is False:
if len(scan.rules) == 0:
detail = (
f"additional_information is a required field as package "
f"`{body.name}@{body.version}` has no matched rules in the database"
)
else:
detail = "additional_information is required when using Observation API"

error = HTTPException(400, detail=detail)
log.error(
"Missing additional_information field", error_message=detail, tag="missing_additional_information"
if len(scan.rules) == 0:
Robin5605 marked this conversation as resolved.
Show resolved Hide resolved
detail = (
f"additional_information is a required field as package "
f"`{body.name}@{body.version}` has no matched rules in the database"
)
raise error
else:
detail = "additional_information is required when using Observation API"

error = HTTPException(400, detail=detail)
log.error("Missing additional_information field", error_message=detail, tag="missing_additional_information")
raise error


def _validate_pypi(name: str, version: str, http_client: httpx.Client):
Expand Down Expand Up @@ -163,9 +159,6 @@ def report_package(
"""
Report a package to PyPI.

The optional `use_email` field can be used to send reports by email. This
defaults to `False`.

There are some restrictions on what packages can be reported. They must:
- exist in the database
- exist on PyPI
Expand Down Expand Up @@ -208,30 +201,18 @@ def report_package(

rules_matched: list[str] = [rule.name for rule in scan.rules]

if body.use_email is True:
report = EmailReport(
name=body.name,
version=body.version,
rules_matched=rules_matched,
recipient=body.recipient,
inspector_url=inspector_url,
additional_information=body.additional_information,
)
# We previously checked this condition, but the typechecker isn't smart
# enough to figure that out
assert body.additional_information is not None

httpx_client.post(f"{mainframe_settings.reporter_url}/report/email", json=jsonable_encoder(report))
else:
# We previously checked this condition, but the typechecker isn't smart
# enough to figure that out
assert body.additional_information is not None

report = ObservationReport(
kind=ObservationKind.Malware,
summary=body.additional_information,
inspector_url=inspector_url,
extra=dict(yara_rules=rules_matched),
)
report = ObservationReport(
kind=ObservationKind.Malware,
summary=body.additional_information,
inspector_url=inspector_url,
extra=dict(yara_rules=rules_matched),
)

httpx_client.post(f"{mainframe_settings.reporter_url}/report/{name}", json=jsonable_encoder(report))
httpx_client.post(f"{mainframe_settings.reporter_url}/report/{name}", json=jsonable_encoder(report))

with session.begin():
scan.reported_by = auth.subject
Expand All @@ -247,7 +228,6 @@ def report_package(
"inspector_url": inspector_url,
"additional_information": body.additional_information,
"rules_matched": rules_matched,
"use_email": body.use_email,
},
reported_by=auth.subject,
)
10 changes: 0 additions & 10 deletions src/mainframe/models/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,16 +95,6 @@ class ReportPackageBody(PackageSpecifier):
recipient: Optional[str]
inspector_url: Optional[str]
additional_information: Optional[str]
use_email: bool = False


class EmailReport(PackageSpecifier):
"""Model for a report using email"""

rules_matched: list[str]
recipient: Optional[str] = None
inspector_url: Optional[str]
additional_information: Optional[str]


# Taken from
Expand Down
65 changes: 17 additions & 48 deletions tests/test_report.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,60 +26,31 @@
from mainframe.json_web_token import AuthenticationData
from mainframe.models.orm import DownloadURL, Rule, Scan, Status
from mainframe.models.schemas import (
EmailReport,
ObservationKind,
ObservationReport,
ReportPackageBody,
)


@pytest.mark.parametrize(
"body,url,expected",
[
(
ReportPackageBody(
name="c",
version="1.0.0",
recipient=None,
inspector_url=None,
additional_information="this package is bad",
use_email=True,
),
"/report/email",
EmailReport(
name="c",
version="1.0.0",
rules_matched=["rule 1", "rule 2"],
inspector_url="test inspector url",
additional_information="this package is bad",
),
),
(
ReportPackageBody(
name="c",
version="1.0.0",
recipient=None,
inspector_url=None,
additional_information="this package is bad",
),
"/report/c",
ObservationReport(
kind=ObservationKind.Malware,
summary="this package is bad",
inspector_url="test inspector url",
extra=dict(yara_rules=["rule 1", "rule 2"]),
),
),
],
)
def test_report(
sm: sessionmaker[Session],
db_session: Session,
auth: AuthenticationData,
body: ReportPackageBody,
url: str,
expected: EmailReport | ObservationReport,
):
body = ReportPackageBody(
name="c",
version="1.0.0",
recipient=None,
inspector_url=None,
additional_information="this package is bad",
)

report = ObservationReport(
kind=ObservationKind.Malware,
summary="this package is bad",
inspector_url="test inspector url",
extra=dict(yara_rules=["rule 1", "rule 2"]),
)
scan = Scan(
name="c",
version="1.0.0",
Expand Down Expand Up @@ -107,7 +78,7 @@ def test_report(

report_package(body, sm(), auth, mock_httpx_client)

mock_httpx_client.post.assert_called_once_with(url, json=jsonable_encoder(expected))
mock_httpx_client.post.assert_called_once_with("/report/c", json=jsonable_encoder(report))

with sm() as sess, sess.begin():
s = sess.scalar(select(Scan).where(Scan.name == "c").where(Scan.version == "1.0.0"))
Expand Down Expand Up @@ -180,14 +151,13 @@ def test_report_inspector_url(body_url: Optional[str], scan_url: Optional[str]):
@pytest.mark.parametrize(
("body", "scan"),
[
( # No additional information, and no rules with email
(
ReportPackageBody(
name="c",
version="1.0.0",
recipient=None,
inspector_url="inspector url override",
additional_information=None,
use_email=True,
),
Scan(
name="c",
Expand All @@ -209,14 +179,13 @@ def test_report_inspector_url(body_url: Optional[str], scan_url: Optional[str]):
commit_hash="test commit hash",
),
),
( # No additional information with Observations
(
ReportPackageBody(
name="c",
version="1.0.0",
recipient=None,
inspector_url="inspector url override",
additional_information=None,
use_email=False,
),
Scan(
name="c",
Expand Down