forked from pixee/codemodder-python
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcodetf.py
More file actions
161 lines (122 loc) · 3.69 KB
/
codetf.py
File metadata and controls
161 lines (122 loc) · 3.69 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
"""
Data models for the CodeTF format.
We need to keep this in sync with the CodeTF schema.
"""
from __future__ import annotations
import os
import sys
from enum import Enum
from typing import TYPE_CHECKING, Optional
from pydantic import BaseModel, model_validator
from codemodder import __version__
from codemodder.logging import logger
if TYPE_CHECKING:
from codemodder.context import CodemodExecutionContext
class Action(Enum):
ADD = "add"
REMOVE = "remove"
class PackageResult(Enum):
COMPLETED = "completed"
FAILED = "failed"
SKIPPED = "skipped"
class DiffSide(Enum):
LEFT = "left"
RIGHT = "right"
class PackageAction(BaseModel):
action: Action
result: PackageResult
package: str
class Change(BaseModel):
lineNumber: int
description: Optional[str]
# All of our changes are currently treated as additive, so it makes sense
# for the comments to appear on the RIGHT side of the split diff. Eventually we
# may want to differentiate between LEFT and RIGHT, but for now we'll just
# default to RIGHT.
diffSide: DiffSide = DiffSide.RIGHT
properties: Optional[dict] = None
packageActions: Optional[list[PackageAction]] = None
class ChangeSet(BaseModel):
"""A set of changes made to a file at `path`"""
path: str
diff: str
changes: list[Change] = []
class Reference(BaseModel):
url: str
description: Optional[str] = None
@model_validator(mode="after")
def validate_description(self):
self.description = self.description or self.url
return self
class Rule(BaseModel):
id: str
name: str
url: Optional[str] = None
class Finding(BaseModel):
id: str
fixed: bool
reason: Optional[str] = None
@model_validator(mode="after")
def validate_reason(self):
assert self.fixed or self.reason, "reason is required if fixed is False"
return self
class DetectionTool(BaseModel):
name: str
rule: Rule
findings: list[Finding] = []
class Result(BaseModel):
codemod: str
summary: str
description: str
detectionTool: Optional[DetectionTool] = None
references: Optional[list[Reference]] = None
properties: Optional[dict] = None
failedFiles: Optional[list[str]] = None
changeset: list[ChangeSet]
class Sarif(BaseModel):
artifact: str
sha1: str
class Run(BaseModel):
vendor: str
tool: str
version: str
projectName: Optional[str] = None
commandLine: str
elapsed: Optional[int]
directory: str
sarifs: list[Sarif] = []
class CodeTF(BaseModel):
run: Run
results: list[Result]
@classmethod
def build(
cls,
context: CodemodExecutionContext,
elapsed_ms,
original_args,
results: list[Result],
):
command_name = os.path.basename(sys.argv[0])
command_args = " ".join(original_args)
run = Run(
vendor="pixee",
tool="codemodder-python",
version=__version__,
projectName=None,
commandLine=f"{command_name} {command_args}",
elapsed=elapsed_ms,
directory=str(context.directory.absolute()),
# TODO: this should be populated from the context
sarifs=[],
)
return cls(run=run, results=results)
def write_report(self, outfile):
try:
with open(outfile, "w", encoding="utf-8") as f:
f.write(self.model_dump_json(exclude_none=True))
except Exception:
logger.exception("failed to write report file.")
# Any issues with writing the output file should exit status 2.
return 2
logger.debug("wrote report to %s", outfile)
return 0