Skip to content

fix(docker): include LICENSE in runtime image for Apache-2.0 attribution#1520

Open
aenawi wants to merge 2 commits intoOWASP:masterfrom
aenawi:fix/dockerfile-include-license-in-runtime-image
Open

fix(docker): include LICENSE in runtime image for Apache-2.0 attribution#1520
aenawi wants to merge 2 commits intoOWASP:masterfrom
aenawi:fix/dockerfile-include-license-in-runtime-image

Conversation

@aenawi
Copy link
Copy Markdown

@aenawi aenawi commented Apr 18, 2026

Summary

Add COPY LICENSE ./ to the runtime stage of the Dockerfile so the Apache-2.0 license text lands inside every built image at /usr/src/owaspnettacker/LICENSE. One line + one comment + one blank line; zero runtime behavior change; ~11KB image size increase.

Why

Apache-2.0 section 4(c) requires distributions of the work or derivative works to retain the license text. The multi-stage runtime image produced by the current Dockerfile ships the virtualenv and the nettacker wheel — both clean — but no LICENSE file ends up at a visible path in the final image.

Verified by building master locally and inspecting:

$ docker run --rm --entrypoint sh nettacker:master -c \
    'ls /usr/src/owaspnettacker/.venv/lib/python3.11/site-packages/nettacker-*.dist-info/'
INSTALLER  METADATA  RECORD  REQUESTED  WHEEL  direct_url.json  entry_points.txt

Seven files, no LICENSE. Every other package in the venv (Flask, SQLAlchemy, cryptography, etc.) ships its LICENSE inside its own dist-info/ — only nettacker's own wheel doesn't. So when a downstream distributor rebuilds the image (e.g. to produce a native linux/arm64 variant for Apple Silicon, since the official hub image is amd64-only today), the rebuilder has to add a separate COPY LICENSE layer on top to stay compliant. That's easy to forget and clumsy to explain.

The change

@@ -47,6 +47,9 @@ ENV PATH=/usr/src/owaspnettacker/.venv/bin:$PATH
 RUN pip install --no-deps --no-cache-dir nettacker-*.whl && \
     rm -f nettacker-*.whl

+### Preserve Apache-2.0 license text in the final image for downstream attribution
+COPY LICENSE ./

 ### We now have Nettacker installed in the virtualenv with 'nettacker' command which is the new entrypoint
 ENV docker_env=true
 ENTRYPOINT [ "nettacker" ]

Placed in the runtime stage (not the builder) so it lands in the final image without affecting the wheel build or the builder-stage cache layers.

Why Dockerfile and not pyproject.toml

Before writing this, I checked whether the cleaner fix would be to include LICENSE inside the wheel's dist-info/ — benefitting pip install nettacker consumers too, not just Docker users. Tried two Poetry include syntaxes against current poetry-core:

# Attempt 1
include = ["LICENSE"]

# Attempt 2
include = [{ path = "LICENSE", format = ["sdist", "wheel"] }]

Neither placed LICENSE into nettacker-*.dist-info/ in the built wheel. Poetry's include directive apparently copies into the wheel's top-level (next to the package directory) but not into dist-info/. The canonical fix at the packaging layer would require either migrating from legacy [tool.poetry] to PEP 621 [project] + license-files = ["LICENSE"], or switching the build backend — a larger refactor than a compliance fix warrants.

Happy to open a separate issue discussing the wheel-side fix if the maintainers would prefer that route long-term. This PR keeps scope to the minimum viable compliance improvement for the published Docker image.

Compatibility

  • Zero change to Nettacker runtime behavior. LICENSE file just exists in the image where a user might docker cp or docker run --entrypoint sh to inspect.
  • Zero change to docker-compose.yml, CI workflows, or any user-facing documented command.
  • Zero change to Python source or the published wheel.
  • Image grows by ~11KB (LICENSE file size) — negligible vs. the venv and wheel already present.

Test plan

  • docker build . succeeds on current master
  • docker run --rm --entrypoint sh <image> -c 'ls -la /usr/src/owaspnettacker/LICENSE' shows the file present (11357 bytes, mode 644)
  • head -3 /usr/src/owaspnettacker/LICENSE inside the image confirms it's the Apache-2.0 text
  • docker run --rm <image> (no args) and docker run --rm <image> --help both behave identically to before (the CMD/ENTRYPOINT pair added in the recent multi-stage refactor is preserved unchanged)

Context

Found this while building native multi-arch rebuilds for Apple Silicon workstations. The ENTRYPOINT fix I was also going to offer is already in master (thank you!) — this PR is just the remaining LICENSE piece. No strong opinion on final form; happy to iterate if the maintainers want the fix approached differently.

The multi-stage runtime image copies in the nettacker wheel and its
virtualenv, but does not preserve the project's LICENSE file. This
matters for downstream rebuilds (e.g. native linux/arm64 republishes
on Apple Silicon, where the official hub image is amd64-only today):
rebuilders have to add a separate COPY LICENSE layer on top to meet
Apache-2.0 section 4(c), which is awkward.

Adding 'COPY LICENSE ./' in the runtime stage puts the license text
at /usr/src/owaspnettacker/LICENSE inside every image. Downstream
rebuilds then inherit the attribution automatically — no extra
layer or post-build tweak needed.

Also attempted a cleaner fix via 'pyproject.toml' (so the wheel's
dist-info would carry LICENSE natively, benefiting every consumer
including 'pip install nettacker'). Both 'include = ["LICENSE"]'
and 'include = [{ path = "LICENSE", format = ["sdist", "wheel"] }]'
were tested with the current poetry-core: neither places the file
in the nettacker-*.dist-info/ directory. Making that work would
require migrating to PEP 621 '[project]' style with 'license-files'
or switching the build backend — a larger change that belongs in
its own PR. This one keeps the minimum viable compliance fix.

One line + one comment. Zero runtime behavior change. Image grows
by ~11KB (LICENSE file size).
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 18, 2026

Summary by CodeRabbit

  • Chores
    • Docker image now includes the project's LICENSE file in the distributed runtime image, ensuring the license is packaged with releases. No functional or runtime behavior changes.

Walkthrough

Added a single line to the Dockerfile that copies the repository LICENSE into the final runtime image's working directory; no other build stages, dependencies, or runtime behavior were modified.

Changes

Cohort / File(s) Summary
Docker Configuration
Dockerfile
Added COPY LICENSE ./ to include the project's LICENSE file in the final image.

Estimated code review effort

🎯 1 (Trivial) | ⏱️ ~2 minutes

Suggested labels

Docker

Suggested reviewers

  • arkid15r
🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: adding LICENSE to the Docker runtime image for Apache-2.0 compliance.
Description check ✅ Passed The description is directly related to the changeset, providing detailed context about why the LICENSE file needs to be included in the Docker image for license attribution compliance.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@securestep9
Copy link
Copy Markdown
Collaborator

@coderabbitai full review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 18, 2026

✅ Actions performed

Full review triggered.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@Dockerfile`:
- Around line 50-52: The PR is missing the required template sections and the
commit 4b390d6f32ad is unsigned; update the PR description to include the three
required headers "Proposed change", "Type of change", and "Checklist" matching
.github/PULL_REQUEST_TEMPLATE.md exactly, then locally sign the offending commit
(or all commits if needed) and force-push: run git commit --amend -S --no-edit
(or git rebase --exec 'git commit --amend --no-edit -S' ... for multiple
commits) and git push --force-with-lease so the commit(s) are GPG-signed and the
PR body complies with the template.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 82a60434-9a6c-4ce7-99e5-edf31f1534b2

📥 Commits

Reviewing files that changed from the base of the PR and between db51ac5 and 4b390d6.

📒 Files selected for processing (1)
  • Dockerfile

Comment thread Dockerfile
Comment on lines +50 to +52
### Preserve Apache-2.0 license text in the final image for downstream attribution
COPY LICENSE ./

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify PR template sections and commit signature status for PR 1520 (read-only)

set -euo pipefail

echo "== PR body =="
gh pr view 1520 --repo OWASP/Nettacker --json body,author,url \
  --jq '.url + "\nAuthor: @" + .author.login + "\n\n" + .body'

echo
echo "== Template section presence check =="
gh pr view 1520 --repo OWASP/Nettacker --json body --jq '.body' | \
python - <<'PY'
import sys
body = sys.stdin.read()
required = ["Proposed change", "Type of change", "Checklist"]
missing = [s for s in required if s.lower() not in body.lower()]
if missing:
    print("Missing sections:", ", ".join(missing))
else:
    print("All required section headers detected.")
PY

echo
echo "== Commit signature verification =="
gh api repos/OWASP/Nettacker/pulls/1520/commits --paginate | \
jq -r '.[] | [.sha[0:12], .commit.verification.verified, (.commit.verification.reason // "unknown")] | `@tsv`'

Repository: OWASP/Nettacker

Length of output: 4589


🏁 Script executed:

#!/bin/bash
# Retry commit signature verification for PR 1520 with timeout handling
timeout 30 gh api repos/OWASP/Nettacker/pulls/1520/commits --jq '.[] | {sha: .sha[0:12], author: .commit.author.name, verified: .commit.verification.verified, reason: .commit.verification.reason}'

Repository: OWASP/Nettacker

Length of output: 106


🏁 Script executed:

#!/bin/bash
# Commit signature verification for PR 1520
gh api repos/OWASP/Nettacker/pulls/1520/commits --jq '.[] | {sha: .sha[0:12], author: .commit.author.name, verified: .commit.verification.verified, reason: .commit.verification.reason}' | head -50

Repository: OWASP/Nettacker

Length of output: 146


PR template non-compliant and commit unsigned — blocks merge.

This PR is missing all three required template sections ("Proposed change", "Type of change", "Checklist"). The PR body uses custom headers instead of .github/PULL_REQUEST_TEMPLATE.md format.

Additionally, the commit (4b390d6 by Hashem Aldhaheri) is unsigned. All commits in this repository must be GPG signed. @aenawi, please:

  1. Rewrite the PR description to match the three required template sections.
  2. Sign the commit and force-push:
    git commit --amend -S --no-edit
    git push --force-with-lease
    
    Or use git rebase --exec 'git commit --amend --no-edit -S' ... if multiple commits need signing.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Dockerfile` around lines 50 - 52, The PR is missing the required template
sections and the commit 4b390d6f32ad is unsigned; update the PR description to
include the three required headers "Proposed change", "Type of change", and
"Checklist" matching .github/PULL_REQUEST_TEMPLATE.md exactly, then locally sign
the offending commit (or all commits if needed) and force-push: run git commit
--amend -S --no-edit (or git rebase --exec 'git commit --amend --no-edit -S' ...
for multiple commits) and git push --force-with-lease so the commit(s) are
GPG-signed and the PR body complies with the template.

@securestep9
Copy link
Copy Markdown
Collaborator

securestep9 commented Apr 18, 2026

@aenawi PRs with unsigned commits and missing linked issue cannot be accepted. Please follow the contribution guidelines and the PR template (which you copy-pasted over with some AI-generated text!) and make sure to sign your commits

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants