Skip to content

Commit edcf0d6

Browse files
authored
Merge pull request #67 from infragov-project/rego_integration
Rego integration on security smells
2 parents d2e2bf0 + 09a70cd commit edcf0d6

File tree

474 files changed

+3865
-765
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

474 files changed

+3865
-765
lines changed

.github/workflows/rego_python.yml

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
name: rego-python
2+
3+
on:
4+
push:
5+
paths:
6+
- ".github/workflows/rego-python.yml"
7+
- "glitch/rego/rego_python/**"
8+
workflow_dispatch:
9+
10+
permissions:
11+
contents: write
12+
13+
jobs:
14+
build-binaries:
15+
name: Build Rego Python binaries
16+
runs-on: ${{ matrix.os_runner }}
17+
strategy:
18+
matrix:
19+
include:
20+
# Linux
21+
- os: linux
22+
arch: amd64
23+
os_runner: ubuntu-latest
24+
- os: linux
25+
arch: arm64
26+
os_runner: ubuntu-24.04-arm
27+
28+
# macOS
29+
- os: darwin
30+
arch: amd64
31+
os_runner: macos-15-intel
32+
- os: darwin
33+
arch: arm64
34+
os_runner: macos-latest
35+
36+
# Windows
37+
- os: windows
38+
arch: amd64
39+
os_runner: windows-latest
40+
#- os: windows
41+
# arch: arm64
42+
# os_runner: windows-11-arm
43+
# This fails the build because the runner is still limited
44+
steps:
45+
- name: Checkout repository
46+
uses: actions/checkout@v4
47+
48+
- name: Set up Go
49+
uses: actions/setup-go@v4
50+
with:
51+
go-version: 'stable'
52+
cache-dependency-path: glitch/rego/rego_python/src/rego_python/go/go.sum
53+
54+
- name: Build binary
55+
shell: bash
56+
run: |
57+
OS=${{ matrix.os }}
58+
ARCH=${{ matrix.arch }}
59+
60+
# Determine file extension
61+
if [ "$OS" = "windows" ]; then
62+
EXT="dll"
63+
elif [ "$OS" = "darwin" ]; then
64+
EXT="dylib"
65+
else
66+
EXT="so"
67+
fi
68+
69+
OUTPUT="../bin/librego-$OS-$ARCH.$EXT"
70+
echo "Building $OUTPUT ..."
71+
72+
cd glitch/rego/rego_python/src/rego_python/go
73+
GOOS=$OS GOARCH=$ARCH go build -o "$OUTPUT" -buildmode=c-shared regolib.go
74+
rm -f ../bin/librego-*.h
75+
76+
- name: List bin folder
77+
run: ls -l glitch/rego/rego_python/src/rego_python/bin/
78+
79+
- name: Upload binary artifact
80+
uses: actions/upload-artifact@v4
81+
with:
82+
name: librego-${{ matrix.os }}-${{ matrix.arch }}
83+
path: glitch/rego/rego_python/src/rego_python/bin/librego-${{ matrix.os }}-${{ matrix.arch }}.*
84+
retention-days: 1
85+
86+
build-package:
87+
name: Build Python package
88+
runs-on: ubuntu-latest
89+
needs: build-binaries
90+
91+
steps:
92+
- uses: actions/checkout@v4
93+
94+
# Download all binary artifacts into bin/
95+
- uses: actions/download-artifact@v4
96+
with:
97+
path: glitch/rego/rego_python/src/rego_python/bin
98+
merge-multiple: true
99+
100+
- name: Set up Python
101+
uses: actions/setup-python@v5
102+
with:
103+
python-version: "3.x"
104+
105+
- name: Install build tools
106+
run: |
107+
python -m pip install --upgrade build twine
108+
109+
- name: Build wheel and sdist
110+
working-directory: glitch/rego/rego_python
111+
run: python -m build
112+
113+
- name: Upload package artifacts
114+
uses: actions/upload-artifact@v4
115+
with:
116+
name: python-package
117+
path: glitch/rego/rego_python/dist/*
118+
retention-days: 1
119+
120+
release:
121+
name: Create GitHub Release
122+
runs-on: ubuntu-latest
123+
needs: build-package
124+
125+
steps:
126+
- name: Checkout repository
127+
uses: actions/checkout@v4
128+
with:
129+
fetch-depth: 0
130+
131+
- name: Extract version from pyproject.toml
132+
id: get_version
133+
run: |
134+
VERSION=$(grep '^version = ' glitch/rego/rego_python/pyproject.toml | sed -E 's/version = "(.*)"/\1/')
135+
echo "version=$VERSION" >> $GITHUB_OUTPUT
136+
echo "Version extracted: $VERSION"
137+
138+
- name: Create tag for release
139+
run: |
140+
git config user.name "rego_python-release-bot"
141+
git config user.email "rego_python@users.noreply.github.com"
142+
TAG=rego_python-v${{ steps.get_version.outputs.version }}
143+
git tag $TAG
144+
git push origin $TAG
145+
146+
NON_PREFIXED_TAG=v${{ steps.get_version.outputs.version }}
147+
if git rev-parse "$NON_PREFIXED_TAG" >/dev/null 2>&1; then
148+
echo "WARNING: Non-prefixed tag $NON_PREFIXED_TAG exists. Consider deleting it to avoid confusion."
149+
fi
150+
env:
151+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
152+
153+
- name: Download built package and binaries
154+
uses: actions/download-artifact@v4
155+
with:
156+
path: release_assets
157+
merge-multiple: true
158+
159+
- name: List downloaded assets
160+
run: |
161+
echo "Listing contents of release_assets directory:"
162+
ls -lR release_assets
163+
164+
- name: Create GitHub Release
165+
uses: softprops/action-gh-release@v2
166+
with:
167+
tag_name: "rego_python-v${{ steps.get_version.outputs.version }}"
168+
name: "Rego Python v${{ steps.get_version.outputs.version }}"
169+
files: |
170+
release_assets/*.whl
171+
release_assets/*.tar.gz
172+
release_assets/*.so
173+
release_assets/*.dylib
174+
release_assets/*.dll
175+
generate_release_notes: false
176+
draft: false
177+
prerelease: false
178+
env:
179+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
180+

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ __pycache__/
55

66
# C extensions
77
*.so
8+
glitch/rego/go/library/librego.h
89

910
# Distribution / packaging
1011
.Python

README.md

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,32 @@ To install GLITCH using Poetry, run:
5858
poetry install
5959
```
6060

61+
### Rego
62+
63+
To do all checks, you need to use Rego. For that, you need the appropriate binary for your environment and architecture.
64+
65+
You can find these binaries in the Rego Python release.
66+
67+
Or, you can compile your own binary, but this requires to have ```go``` installed. To do that, run the following commands on the root folder:
68+
69+
```
70+
OS={Operating System}
71+
ARCH=${{ Architecture }}
72+
# Determine file extension
73+
if [ "$OS" = "windows" ]; then
74+
EXT="dll"
75+
elif [ "$OS" = "darwin" ]; then
76+
EXT="dylib"
77+
else
78+
EXT="so"
79+
fi
80+
81+
OUTPUT="../bin/librego-$OS-$ARCH.$EXT"
82+
83+
cd glitch/rego/rego_python/src/rego_python/go
84+
GOOS=$OS GOARCH=$ARCH go build -o "$OUTPUT" -buildmode=c-shared regolib.go
85+
```
86+
6187
**WARNING**: _For now, the GLITCH VSCode extension does not function if GLITCH
6288
is installed via Poetry. Since Poetry uses virtual environments it does not
6389
create a binary for GLITCH available in the user's PATH, which is required for
@@ -92,9 +118,9 @@ glitch lint --help
92118

93119
## Tests
94120

95-
To run the tests for GLITCH go to the folder ```glitch``` and run:
121+
To run the tests for GLITCH run the following command:
96122
```
97-
python -m unittest discover tests
123+
poetry run pytest -s
98124
```
99125

100126
## Configs

glitch/__main__.py

Lines changed: 55 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from pathlib import Path
77
from typing import Tuple, List, Set, Optional, TextIO, Dict, Any
88
from glitch.analysis.rules import Error, RuleVisitor
9-
from glitch.helpers import get_smell_types, get_smells
9+
from glitch.helpers import get_smell_types, get_smells, ini_to_json_dict
1010
from glitch.parsers.docker import DockerParser
1111
from glitch.stats.print import print_stats
1212
from glitch.stats.stats import FileStats
@@ -23,6 +23,7 @@
2323
from pkg_resources import resource_filename
2424
from copy import deepcopy
2525
from concurrent.futures import ThreadPoolExecutor, Future, as_completed
26+
from glitch.rego.engine import load_rego_from_path, run_analyses
2627

2728

2829
# NOTE: These are necessary in order for python to load the visitors.
@@ -38,20 +39,62 @@ def __parse_and_check(
3839
parser: Parser,
3940
analyses: List[RuleVisitor],
4041
stats: FileStats,
42+
config_rego: str,
43+
rego_modules: Dict[str,str]
4144
) -> Set[Error]:
4245
errors: Set[Error] = set()
4346
inter = parser.parse(path, type, module)
4447
# Avoids problems with multiple threads (and possibly multiple files)
4548
# sharing the same object
49+
4650
analyses = deepcopy(analyses)
47-
4851
if inter != None:
4952
for analysis in analyses:
5053
errors.update(analysis.check(inter))
54+
55+
inputRego = json.dumps(inter.as_dict(), indent=2)
56+
57+
errors.update(run_analyses(inputRego, config_rego, rego_modules))
58+
5159
stats.compute(inter)
5260

5361
return errors
5462

63+
def __filter_analysis(
64+
smell_types: Tuple[str, ...],
65+
config: str,
66+
tech: Tech
67+
) -> Tuple[Dict[str,str], List[RuleVisitor]]:
68+
rego_modules: Dict[str,str] = {}
69+
python_analyses: List[RuleVisitor] = []
70+
71+
if not os.path.exists("./glitch/rego/queries/library/glitch_lib.rego"):
72+
raise FileNotFoundError("The rego query library does not exist.")
73+
load_rego_from_path("./glitch/rego/queries/library/glitch_lib.rego", rego_modules)
74+
75+
for smell_type in smell_types:
76+
smells: List[str] = get_smells([smell_type], tech)
77+
fallback: Set[str] = set()
78+
79+
for smell in smells:
80+
if os.path.exists(f"./glitch/rego/queries/{smell_type}/{smell}.rego"):
81+
load_rego_from_path(f"./glitch/rego/queries/{smell_type}/{smell}.rego", rego_modules)
82+
else:
83+
fallback.add(smell)
84+
85+
if len(fallback) > 0:
86+
match smell_type:
87+
case "design":
88+
visitor = DesignVisitor(tech, fallback)
89+
case "security":
90+
visitor = SecurityVisitor(tech, fallback)
91+
case _:
92+
raise ValueError(f"Invalid smell type: {smell_type}")
93+
94+
visitor.config(config)
95+
python_analyses.append(visitor)
96+
97+
return rego_modules, python_analyses
5598

5699
def __get_tech(tech: str) -> Tech:
57100
for t in Tech:
@@ -217,7 +260,7 @@ def lint(
217260
output: Optional[str],
218261
table_format: str,
219262
linter: bool,
220-
n_workers: int,
263+
n_workers: int
221264
):
222265
tech: Tech = __get_tech(tech)
223266
type = UnitBlockType(type)
@@ -238,17 +281,15 @@ def lint(
238281
if tech == Tech.terraform:
239282
config = resource_filename("glitch", "configs/terraform.ini")
240283
file_stats = FileStats()
241-
284+
242285
if smell_types == ():
243286
smell_types = get_smell_types()
244287

245-
analyses: List[RuleVisitor] = []
246-
rules = RuleVisitor.__subclasses__()
247-
for r in rules:
248-
if smell_types == () or r.get_name() in smell_types:
249-
analysis = r(tech)
250-
analysis.config(config)
251-
analyses.append(analysis)
288+
config_rego = ini_to_json_dict(config)
289+
290+
temp = __filter_analysis(smell_types, config, tech)
291+
rego_modules: Dict[str,str] = temp[0] # type: ignore
292+
analyses: List[RuleVisitor] = temp[1]
252293

253294
errors: List[Error] = []
254295
paths: Set[str]
@@ -261,12 +302,14 @@ def lint(
261302
for p in paths:
262303
futures.append(
263304
executor.submit(
264-
__parse_and_check, type, p, module, parser, analyses, file_stats
305+
__parse_and_check, type, p, module, parser, analyses, file_stats, config_rego, rego_modules
265306
)
266307
)
267308
future_to_path[futures[-1]] = p
268309

269310
f = sys.stdout if output is None else open(output, "w")
311+
if csv:
312+
print("PATH,LINE,ERROR,CODE,DESCRIPTION", file=f)
270313
for future in tqdm.tqdm(as_completed(futures), total=len(futures), desc=title):
271314
try:
272315
new_errors = future.result()

glitch/analysis/design/duplicate_block.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from glitch.repr.inter import *
55

66

7-
class UnguardedVariable(DesignSmellChecker):
7+
class DuplicateBlock(DesignSmellChecker):
88
def __get_line(self, i: int, lines: List[Tuple[int, int]]):
99
for j, line in lines:
1010
if i < j:

0 commit comments

Comments
 (0)