Skip to content
Open
Show file tree
Hide file tree
Changes from 36 commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
fb8afb7
add swarm and nomad parse exceptions
sfondev Apr 11, 2025
0098d97
add Nomad parser
sfondev Apr 11, 2025
ae6b6b1
add Swarm parser
sfondev Apr 11, 2025
5a1e69b
fix incorrect exception name
sfondev Apr 11, 2025
4c0178c
format swarm.py
sfondev Apr 25, 2025
10dd8e6
change Terraform parser get_element_info method visibility to protect…
sfondev Apr 25, 2025
602191f
changed the Transformer for Nomad to be a new class that extends the …
sfondev Apr 25, 2025
0adf8ae
format nomad.py
sfondev Apr 25, 2025
f5089f4
format nomad.py and fix missing abs file path issue
sfondev Jun 27, 2025
759ff54
fixes swarm.py adds support for 'extends' and 'includes' extension
sfondev Jun 27, 2025
899368c
fix docker_images_scraper.py: add support for the new search api and …
sfondev Jun 27, 2025
9680335
remove forgotten print 😅
sfondev Jul 17, 2025
7031412
fix boolean parsing
sfondev Jul 17, 2025
c32defa
swarm and nomad tech introduction
sfondev Aug 23, 2025
4cd8574
fix add more common tar compressed with bzip2 extension, also for zst…
sfondev Aug 23, 2025
602602b
adding the smells error messages
sfondev Aug 23, 2025
808439c
swarm and nomad parsers fixes
sfondev Aug 23, 2025
4d2fbd9
correctly escape the '%' character for copying into latex
sfondev Aug 23, 2025
5c67131
adding implementation of Log Collection and some of container image s…
sfondev Aug 23, 2025
253dc45
add deprecated official images smell
sfondev Aug 23, 2025
e178edc
added files needed for log collector/log aggregator smell
sfondev Aug 23, 2025
7ad6a8d
currently useless file, will remove later
sfondev Aug 23, 2025
d7177d4
currently useless file, will remove later
sfondev Aug 23, 2025
1e19b3d
added file needed for the unstable image smell
sfondev Aug 23, 2025
3a49007
adding no api gateway smell files
sfondev Aug 23, 2025
94712dc
adding priveleged containers, mounted docker socket and Nomad integri…
sfondev Aug 23, 2025
8a24153
adding multiple services per deployment unit and missing healthcheck …
sfondev Aug 23, 2025
f804267
fix wrong version of non official image smell detection
sfondev Aug 23, 2025
757d8c8
fix alternative ways of referring to an official image from Docker Hub
sfondev Aug 24, 2025
f8765a3
fix logic error and always make tag lower, since it can be mixed or c…
sfondev Aug 24, 2025
e9671bd
fix small parser mistake
sfondev Aug 24, 2025
85ee60a
try to fix self include when extending from another file
sfondev Aug 24, 2025
3f57889
format + extends should be fixed now...
sfondev Aug 24, 2025
27b9f6b
remove unused stuff
sfondev Aug 25, 2025
9a683b9
fix uniformize use of container image string element instead of whole…
sfondev Aug 25, 2025
060f0e5
fix rename error key
sfondev Aug 25, 2025
e7cfd7c
removed unused commented code
sfondev Aug 25, 2025
948e374
removed parsing of included files, was not a correct approach for sca…
sfondev Aug 25, 2025
663556d
rename class and add missing detection
sfondev Aug 25, 2025
52d225c
rename file
sfondev Aug 25, 2025
85a8fe9
fix typo in class name
sfondev Aug 25, 2025
ded778e
moved the detection of nomad no integrity check together with the others
sfondev Aug 25, 2025
0594562
fix: move smell checkers out of visitor.py
sfondev Aug 25, 2025
5b6cd01
move container image name parser util to separated file
sfondev Aug 25, 2025
8bd9632
format swarm.py
sfondev Aug 25, 2025
ae48806
fix matching invalid ip bindings in complete command invocations, by …
sfondev Aug 28, 2025
0ade21c
fix typo
sfondev Aug 28, 2025
f8ce625
remove hack, fix variable parsing in swarm
sfondev Aug 28, 2025
7ceb362
change to use StringChecker and add handling of arrays and hashes
sfondev Aug 28, 2025
365c906
fix empty strings in env var value
sfondev Aug 28, 2025
aef512b
fix some of SecurityVisitor global constants not being initialized fo…
sfondev Aug 28, 2025
f1ccef5
add: swarm tests
sfondev Aug 28, 2025
578bb59
add missing __init__.py needed for test detection
sfondev Aug 28, 2025
9d12ba3
fix filenames of official docker images and deprecated official image…
sfondev Aug 29, 2025
e6150d4
update official docker images list
sfondev Aug 29, 2025
cb0e621
remove unnecessary method
sfondev Aug 29, 2025
200e9dc
remove unused import
sfondev Aug 29, 2025
febd84d
fix: consider both list of official images. The newer list of officia…
sfondev Aug 30, 2025
374c39f
format visitor.py
sfondev Aug 30, 2025
8a5f19e
fix missing healthchecks smell and wobbly service interaction on nomad
sfondev Aug 30, 2025
fa902f5
add nomad tests
sfondev Aug 30, 2025
4ee287e
fix: add hack to avoid using detection function tailored to nomad for…
sfondev Aug 30, 2025
9835c68
fix name of nomad task config logging driver attribute
sfondev Sep 4, 2025
7caa60e
hack: support multiple artifacts missing integrity checks in Nomad
sfondev Sep 9, 2025
6d9cad3
fix: pinned container image without digest smell
sfondev Sep 9, 2025
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
6 changes: 6 additions & 0 deletions glitch/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
from glitch.parsers.puppet import PuppetParser
from glitch.parsers.terraform import TerraformParser
from glitch.parsers.gha import GithubActionsParser
from glitch.parsers.swarm import SwarmParser
from glitch.parsers.nomad import NomadParser
from glitch.exceptions import throw_exception
from glitch.repair.interactive.main import run_infrafix
from pkg_resources import resource_filename
Expand Down Expand Up @@ -90,6 +92,10 @@ def __get_parser(tech: Tech) -> Parser:
return TerraformParser()
elif tech == Tech.gha:
return GithubActionsParser()
elif tech == Tech.swarm:
return SwarmParser()
elif tech == Tech.nomad:
return NomadParser()
else:
raise ValueError(f"Invalid tech: {tech}")

Expand Down
25 changes: 25 additions & 0 deletions glitch/analysis/rules.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,31 @@ class Error:
Tech.docker: {
"sec_non_official_image": "Use of non-official Docker image - Use of non-official images should be avoided or taken into careful consideration. (CWE-829)",
},
Tech.swarm: {
"sec_non_official_image": "Use of non-official Docker image - Use of non-official images should be avoided or taken into careful consideration. (CWE-829)",
"sec_image_integrity": "Use of image is not tagged with digest - The images downloaded from the internet should be checked. (CWE-353)",
"sec_unstable_tag": "Unstable version/release image tag - Prefer specifying a release version or better yet a specific version digest (CWE-353)",
"sec_no_image_tag": "The image is not tagged - Prefer specifying a release version or better yet specific version digest",
"arc_no_apig": "No API Gateway - If following a microservices architecture you should use in front of your services an API Gateway instead of directly exposing them.",
"arc_no_logging": "Log Collection not found - It is advised to setup logging for your services.",
"arc_missing_healthchecks": "Missing Healthchecks - You should setup healthchecks for your services, or check if the images used already have default ones.",
"sec_mounted_docker_socket": "Docker socket mounted to a container - Avoid mounting the Docker socket to container, if the container is compromised its access to the Docker socket allows control of all other containers, and even acquiring control of the host machine.",
"sec_privileged_containers": "Use of Privileged Containers - Developers should always try to give and use the least privileges possible. Use of privileged containers severely thins out the security and isolation provided by container runtimes, its use should be avoided as much as possible (CWE-250)",
"sec_depr_off_imgs" : "Use of deprecated official Docker images - Use of official deprecated images should be avoided as it makes you open to vulnerabilities, quality issues and unfixed bugs (CWE-1104)",
},
Tech.nomad: {
"sec_non_official_image": "Use of non-official Docker image - Use of non-official images should be avoided or taken into careful consideration. (CWE-829)",
"sec_image_integrity": "Container image is not tagged with digest - The images downloaded from the internet should be checked. (CWE-353)",
"sec_unstable_tag": "Unstable version/release image tag - Prefer specifying a release version or better yet a specific version digest (CWE-353)",
"sec_no_image_tag": "Container Image is not tagged - Prefer specifying a release version or better yet specific version digest (CWE-353)",
"arc_no_apig": "No API Gateway - If following a microservices architecture you should use in front of your services an API Gateway instead of directly exposing them.",
"arc_no_logging": "Log Collection not found - It is advised to setup logging for your services.",
"arc_missing_healthchecks": "Missing Healthchecks - You should setup healthchecks for your services, or check if the images used already have default ones.",
"sec_mounted_docker_socket": "Docker socket mounted to a container - Avoid mounting the Docker socket to container, if the container is compromised its access to the Docker socket allows control of all other containers, and even acquiring control of the host machine.",
"sec_privileged_containers": "Use of Privileged Containers - Developers should always try to give and use the least privileges possible. Use of privileged containers severely thins out the security and isolation provided by container runtimes, its use should be avoided as much as possible (CWE-250)",
"arc_multiple_services": "Multiple Services per Deployment Unit - If you are following a Microservices architecture you are violating the idependent deployability rule by deploying multiple microservices in the same group.",
"sec_depr_off_imgs" : "Use of deprecated official Docker images - Use of official deprecated images should be avoided as it makes you open to vulnerabilities, quality issues and unfixed bugs (CWE-1104)",
},
Tech.terraform: {
"sec_integrity_policy": "Integrity Policy - Image tag is prone to be mutable or integrity monitoring is disabled. (CWE-471)",
"sec_ssl_tls_policy": "SSL/TLS/mTLS Policy - Developers should use SSL/TLS/mTLS protocols and their secure versions. (CWE-326)",
Expand Down
46 changes: 46 additions & 0 deletions glitch/analysis/security/deprecated_official_images.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
from glitch.analysis.rules import Error
from glitch.analysis.security.smell_checker import SecuritySmellChecker
from glitch.analysis.security.visitor import SecurityVisitor
from glitch.repr.inter import CodeElement, KeyValue, Hash, String
from typing import List


class DeprecatedOfficialDockerImages(SecuritySmellChecker):
def check(self, element: CodeElement, file: str) -> List[Error]:
errors: List[Error] = []
image = ""
bad_element = element
if isinstance(element, KeyValue) and element.name == "image":
if isinstance(element.value, String):
image = element.value.value
elif (
isinstance(element, KeyValue)
and element.name == "config"
and isinstance(element.value, Hash)
):
for k, v in element.value.value.items():
if isinstance(k, String) and k.value == "image":
image = v.value
bad_element = v
break
if image != "":
img_name, _, _ = SecurityVisitor.image_parser(image)
for obsolete_img in SecurityVisitor.DEPRECATED_OFFICIAL_DOCKER_IMAGES:
obsolete_img_dockerio = f"docker.io/library/{obsolete_img}"
obsolete_img_library = f"library/{obsolete_img}"
obsolete_img_complete_link = (
f"registry.hub.docker.com/library/{obsolete_img}"
)

if (
img_name == obsolete_img
or img_name == obsolete_img_dockerio
or img_name == obsolete_img_library
or img_name == obsolete_img_complete_link
):
errors.append(
Error("sec_depr_off_imgs", bad_element, file, repr(bad_element))
)
break

return errors
46 changes: 46 additions & 0 deletions glitch/analysis/security/docker_socket_mounted.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
from glitch.analysis.rules import Error
from glitch.analysis.security.smell_checker import SecuritySmellChecker
from glitch.repr.inter import CodeElement, KeyValue, Hash, String,Array
from typing import List


class DockerSocketMountedInsideContainerUse(SecuritySmellChecker):
def check(self, element: CodeElement, file: str) -> List[Error]:
errors: List[Error] = []
if isinstance(element, KeyValue):
if element.name == "volumes" and isinstance(element.value, Array):
for volume in element.value.value:
if isinstance(volume, String) and volume.value.split(":")[
0
].startswith("/var/run/docker.sock"):
errors.append(
Error(
"sec_mounted_docker_socket", volume, file, repr(volume)
)
)
break
elif element.name == "config" and isinstance(element.value, Hash):
found_socket_exposed = False
for k, v in element.value.value.items():
if (
isinstance(k, String)
and k.value == "volumes"
and isinstance(v, Array)
):
for volume in v.value:
if isinstance(volume, String) and volume.value.split(":")[
0
].startswith("/var/run/docker.sock"):
errors.append(
Error(
"sec_mounted_docker_socket",
volume,
file,
repr(volume),
)
)
found_socket_exposed = True
break
if found_socket_exposed:
break
return errors
10 changes: 8 additions & 2 deletions glitch/analysis/security/hard_secr.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,13 @@ def __check_pair(
SecurityVisitor.PASSWORDS + SecurityVisitor.SECRETS + SecurityVisitor.USERS
):
secr_checker = StringChecker(
lambda s: re.match(
lambda s: (re.match(
r"[_A-Za-z0-9$\/\.\[\]-]*{text}\b".format(text=item), s
)
is not None
is not None) or (re.match(
r"[_A-Za-z0-9$\/\.\[\]-]*{text}\b".format(text=item.upper()), s
)
is not None)
)
if secr_checker.check(name) and not whitelist_checker.check(name):
if not var_checker.check(value):
Expand Down Expand Up @@ -77,6 +80,9 @@ def check(self, element: CodeElement, file: str) -> List[Error]:
)
elif isinstance(element, KeyValue) and isinstance(element.value, Hash):
for key, value in element.value.value.items():
#HACK: ignore cases where values are obtained via environment variables
if isinstance(value,String) and value.value.startswith("${") and value.value.endswith("}"):
continue
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why? Shouldn't this be parsed to a variable?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think so, I will try to fix it in the parser

errors += self.__check_pair(element, key, value, file)

return errors
43 changes: 43 additions & 0 deletions glitch/analysis/security/multiple_services_per_deplyment_unit.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
from glitch.analysis.rules import Error
from glitch.analysis.security.smell_checker import SecuritySmellChecker
from glitch.analysis.security.visitor import SecurityVisitor
from glitch.repr.inter import CodeElement, Hash, String, UnitBlock, UnitBlockType
from typing import List


class MultipleServicesPerDeploymentUnitCheck(SecuritySmellChecker):
def check(self, element: CodeElement, file: str) -> List[Error]:
# FIXME: Besides log collectors, there are other types of agents/sidecars for observability (some of which are also on the log collector list)
# and proxies which should also be allowed besides the main microservice
errors: List[Error] = []
if isinstance(element, UnitBlock) and element.type == UnitBlockType.block:
main_service_found = False
for au in element.atomic_units:
if au.type in ["task.docker", "task.podman"]:
image_name = ""
for att in au.attributes:
if att.name == "config" and isinstance(att.value, Hash):
for k, v in att.value.value.items():
if isinstance(k, String) and k.value == "image":
image_name, _, _ = SecurityVisitor.image_parser(
v.value
)
break
if image_name != "":
break

if image_name in SecurityVisitor.LOG_AGGREGATORS_AND_COLLECTORS:
continue

elif main_service_found:
errors.append(
Error("arc_multiple_services", au, file, repr(au))
)
else:
main_service_found = True
elif main_service_found:
# when there are other types of tasks that aren't docker or podman based
# and one that is likely the main microservice has already been found
errors.append(Error("arc_multiple_services", au, file, repr(au)))

return errors
127 changes: 127 additions & 0 deletions glitch/analysis/security/no_api_gateway.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
from glitch.analysis.rules import Error
from glitch.analysis.security.smell_checker import SecuritySmellChecker
from glitch.analysis.security.visitor import SecurityVisitor
from glitch.repr.inter import (
CodeElement,
Hash,
Array,
VariableReference,
String,
UnitBlock,
UnitBlockType,
)
from typing import List, Dict, Any


class NoAPIGatewayCheck(SecuritySmellChecker):
def check(self, element: CodeElement, file: str) -> List[Error]:
errors: List[Error] = []
# Tries to follow an logic similar to the one presented for Kubernetes pods ond doi: 10.5220/0011845500003488

if isinstance(element, UnitBlock) and element.type == UnitBlockType.block:
has_api_gateway = False

network_info: Dict[str, Any] = {
"mode": "bridge", # default network mode
"ports": [],
}

network_mode_element = None
for att in element.attributes:
if att.name == "network" and isinstance(att.value, Hash):
for k, v in att.value.value.items():
if isinstance(k, String) and k.value == "mode":
if v.value == "host":
network_mode_element = v
network_info["mode"] = v.value
elif (
isinstance(k, String) or isinstance(k, VariableReference)
) and k.value == "port":
port_info: Dict[str, Any] = {
"name": "",
}
for _k, _v in v.value.items():
if isinstance(_k, String) and _k.value == "port":
port_info["name"] = _v.value
elif isinstance(_k, String) and _k.value in [
"static",
"to",
]:
port_info[_k.value] = _v.value
network_info["ports"].append(port_info)

for au in element.atomic_units:
for att in au.attributes:
if att.name == "config" and isinstance(att.value, Hash):
temp_errors: List[Error] = []
is_api_gateway = False

if (
isinstance(au.name, String)
and "gateway" in au.name.value.strip().lower()
):
is_api_gateway = True
has_api_gateway = True

for k, v in att.value.value.items():
if (
isinstance(k, String)
and k.value == "ports"
and isinstance(v, Array)
and not is_api_gateway
):
for port in v.value:
if isinstance(port, String):
for exp_port in network_info["ports"]:
if exp_port["name"] == port.value:
if network_info["mode"] == "host":
temp_errors.append(
Error(
"arc_no_apig",
port,
file,
repr(port),
)
)

elif network_info[
"mode"
] == "bridge" and (
"to" in exp_port.keys()
or "static" in exp_port.keys()
):
temp_errors.append(
Error(
"arc_no_apig",
port,
file,
repr(port),
)
)

if isinstance(k, String) and k.value == "image":
image_name, _, _ = SecurityVisitor.image_parser(v.value)
if (
image_name in SecurityVisitor.API_GATEWAYS
or is_api_gateway
):
is_api_gateway = True
has_api_gateway = True
else:
errors += temp_errors
temp_errors = []

if not is_api_gateway:
errors += temp_errors

if not has_api_gateway and network_info["mode"] == "host":
errors.append(
Error(
"arc_no_apig",
network_mode_element,
file,
repr(network_mode_element),
)
)

return errors
Loading