Skip to content

Commit 982d65e

Browse files
author
Dmitry Protsenko
committed
2.0.11 Added a new inspection:
- Use apk add with --no-cache
1 parent bf3651b commit 982d65e

File tree

10 files changed

+107
-1
lines changed

10 files changed

+107
-1
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,13 @@
22

33
# Cloud (IaC) Security Changelog
44

5+
## [2.0.11] 30-08-2025
6+
7+
### Added
8+
- Docker Inspection: [Use apk add with --no-cache](https://protsenko.dev/infrastructure-security/simplify-your-apk-cache-clean-strategy/)
9+
10+
Read more about every implemented [Docker Best Practices](https://protsenko.dev/docker-best-practices-to-secure-and-optimize-your-containers/) in the plugin.
11+
512
## [2.0.10] 23-08-2025
613

714
### Changed

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
pluginGroup = dev.protsenko.securityLinter
22
pluginName = Cloud (IaC) Security
33
pluginRepositoryUrl = https://github.com/NordCoderd/cloud-security-plugin
4-
pluginVersion = 2.0.10
4+
pluginVersion = 2.0.11
55

66
# Supported build number ranges and IntelliJ Platform versions -> https://plugins.jetbrains.com/docs/intellij/build-number-ranges.html
77
pluginSinceBuild = 231
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package dev.protsenko.securityLinter.docker.checker
2+
3+
import dev.protsenko.securityLinter.docker.checker.core.RunCommandValidator
4+
5+
object ApkNoCacheValidator : RunCommandValidator {
6+
private val VALID_COMMAND_PATTERN =
7+
Regex(
8+
"^\\s*RUN(?:(?!.*apk add)|(?=.*--no-cache))(?!.*apk update)(?!.*rm\\s+.*?/var/cache/apk/\\*).*$",
9+
RegexOption.DOT_MATCHES_ALL,
10+
)
11+
12+
override fun isValid(command: String): Boolean = VALID_COMMAND_PATTERN.matches(command)
13+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package dev.protsenko.securityLinter.docker.inspection.run.impl
2+
3+
import com.intellij.codeInspection.ProblemHighlightType
4+
import com.intellij.codeInspection.ProblemsHolder
5+
import com.intellij.psi.PsiElement
6+
import dev.protsenko.securityLinter.core.HtmlProblemDescriptor
7+
import dev.protsenko.securityLinter.core.SecurityPluginBundle
8+
import dev.protsenko.securityLinter.docker.checker.ApkNoCacheValidator
9+
import dev.protsenko.securityLinter.docker.inspection.run.core.DockerfileRunAnalyzer
10+
11+
class ApkNoCacheValidatorAnalyzer : DockerfileRunAnalyzer {
12+
override fun handle(
13+
runCommand: String,
14+
psiElement: PsiElement,
15+
holder: ProblemsHolder,
16+
) {
17+
if (!ApkNoCacheValidator.isValid(runCommand)) {
18+
val descriptor =
19+
HtmlProblemDescriptor(
20+
psiElement,
21+
SecurityPluginBundle.message("dfs030.documentation"),
22+
SecurityPluginBundle.message("dfs030.problem-text"),
23+
ProblemHighlightType.WARNING,
24+
)
25+
26+
holder.registerProblem(descriptor)
27+
}
28+
}
29+
}

src/main/resources/META-INF/plugin.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@
9696
<dockerFileRunAnalyzer implementation="dev.protsenko.securityLinter.docker.inspection.run.impl.ZypperInstallWithoutCleanAnalyzer"/>
9797
<dockerFileRunAnalyzer implementation="dev.protsenko.securityLinter.docker.inspection.run.impl.AptIsUsedAnalyzer"/>
9898
<dockerFileRunAnalyzer implementation="dev.protsenko.securityLinter.docker.inspection.run.impl.UserAddAnalyzer"/>
99+
<dockerFileRunAnalyzer implementation="dev.protsenko.securityLinter.docker.inspection.run.impl.ApkNoCacheValidatorAnalyzer"/>
99100

100101
<dockerFileExposeAnalyzer implementation="dev.protsenko.securityLinter.docker.inspection.expose.impl.SshPortExposedAnalyzer"/>
101102
<dockerFileExposeAnalyzer implementation="dev.protsenko.securityLinter.docker.inspection.expose.impl.ExposedPortOutOfRangeAnalyzer"/>

src/main/resources/messages/SecurityPluginBundle.properties

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,9 @@ dfs028.multiple-consecutive-run-commands=Multiple consecutive 'RUN' instructions
109109
### ds035 -> dfs029
110110
dfs029.documentation=useradd-missing-l-flag-high-uid
111111
dfs029.useradd-missing-l-flag-high-uid='useradd' without the '-l' flag and a high UID may lead to an excessively large image.
112+
### dfs030
113+
dfs030.documentation=simplify-your-apk-cache-clean-strategy
114+
dfs030.problem-text=Alpine package installation without --no-cache, consider using it to reduce image size.
112115

113116
### dcs001 [only docker-compose]
114117
ds033.using-privileged=Using privileged: true grants full root access to the host, bypassing isolation mechanisms.
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package dev.protsenko.securityLinter.docker
2+
3+
import com.intellij.codeInspection.LocalInspectionTool
4+
import dev.protsenko.securityLinter.core.DockerHighlightingBaseTest
5+
import dev.protsenko.securityLinter.docker.inspection.run.DockerfileRunInspection
6+
7+
class DFS030ApkNoCacheTest(
8+
override val ruleFolderName: String = "DFS030",
9+
override val customFiles: Set<String> = emptySet(),
10+
override val targetInspection: LocalInspectionTool = DockerfileRunInspection(),
11+
) : DockerHighlightingBaseTest()
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package dev.protsenko.securityLinter.utils
2+
3+
import dev.protsenko.securityLinter.docker.checker.ApkNoCacheValidator
4+
import junit.framework.TestCase
5+
6+
class ApkNoCacheApkNoCacheValidatorTest : TestCase() {
7+
fun testValidCommands() {
8+
assertTrue(ApkNoCacheValidator.isValid("RUN echo 'Hello World'"))
9+
assertTrue(ApkNoCacheValidator.isValid("RUN apk add --no-cache git"))
10+
assertTrue(ApkNoCacheValidator.isValid("RUN apk --no-cache add git"))
11+
val multiLineValid =
12+
"""
13+
RUN apk add --no-cache --virtual .build-deps gcc musl-dev && \
14+
./configure && \
15+
make && make install
16+
""".trimIndent()
17+
assertTrue(ApkNoCacheValidator.isValid(multiLineValid))
18+
assertTrue(ApkNoCacheValidator.isValid("RUN ./autoupdate-script.sh"))
19+
assertTrue(ApkNoCacheValidator.isValid(" RUN apk add --no-cache git"))
20+
}
21+
22+
fun testInvalidCommands() {
23+
assertFalse(ApkNoCacheValidator.isValid("RUN apk add git"))
24+
assertFalse(ApkNoCacheValidator.isValid("RUN apk update && apk add git"))
25+
assertFalse(ApkNoCacheValidator.isValid("RUN apk update"))
26+
assertFalse(ApkNoCacheValidator.isValid("RUN apk add git && rm -rf /var/cache/apk/*"))
27+
assertFalse(ApkNoCacheValidator.isValid("RUN apk add git && rm -rf /var/cache/apk/*"))
28+
val multiLineInvalid =
29+
"""
30+
RUN apk add gcc musl-dev && \
31+
./configure
32+
""".trimIndent()
33+
assertFalse(ApkNoCacheValidator.isValid(multiLineInvalid))
34+
assertFalse(ApkNoCacheValidator.isValid("apk add --no-cache git"))
35+
assertFalse(ApkNoCacheValidator.isValid(""))
36+
}
37+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
FROM alpine:3.13
2+
RUN apk add --no-cache git
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
FROM alpine:3.13
2+
<warning descr="Alpine package installation without --no-cache, consider using it to reduce image size."><warning descr="Multiple consecutive 'RUN' instructions. Consider consolidation.">RUN apk add git</warning></warning>
3+
<warning descr="Alpine package installation without --no-cache, consider using it to reduce image size."><warning descr="Multiple consecutive 'RUN' instructions. Consider consolidation.">RUN apk add git && rm -rf /var/cache/apk/*</warning></warning>

0 commit comments

Comments
 (0)