From da2d30f101da3f8b73f88ccaba6e333ab7dc40a5 Mon Sep 17 00:00:00 2001 From: Marcel Arns Date: Wed, 8 Nov 2023 14:39:54 +0100 Subject: [PATCH 1/2] refactor(action): rename function for cache-control --- s3_artifact/action.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/s3_artifact/action.py b/s3_artifact/action.py index 53a239f..d04ed43 100644 --- a/s3_artifact/action.py +++ b/s3_artifact/action.py @@ -58,7 +58,7 @@ def deploy(config: S3ArtifactConfig, artifacts_s3_path: str, environment: str) - def upload(config: S3ArtifactConfig, target: str) -> Sequence[str]: - def _prepare_metadata_command(cache_config: S3ArtifactCustomMetadataConfig): + def _prepare_cache_control_and_content_type_command(cache_config: S3ArtifactCustomMetadataConfig): content_type = cache_config.mime_type or DEFAULT_MIME_TYPES[Path(cache_config.path).suffix] content_type_option = f"--content-type '{content_type}'" @@ -72,7 +72,10 @@ def _get_default_cache_control(): return ( f"aws s3 sync {config.local_artifacts_path} {target} {_get_default_cache_control()} {S3_SYNC_OPTIONS}", - *(_prepare_metadata_command(custom_metadata) for custom_metadata in config.custom_metadata), + *( + _prepare_cache_control_and_content_type_command(custom_metadata) + for custom_metadata in config.custom_metadata + ), ) From c5a364aae17ae3b914c817bc22b63f9dc40ab293 Mon Sep 17 00:00:00 2001 From: Marcel Arns Date: Wed, 8 Nov 2023 14:40:14 +0100 Subject: [PATCH 2/2] feat(action): set additional security headers for html files --- s3_artifact/action.py | 10 ++++++++++ tests/test_action.py | 21 +++++++++++++++++++-- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/s3_artifact/action.py b/s3_artifact/action.py index d04ed43..eb1fa52 100644 --- a/s3_artifact/action.py +++ b/s3_artifact/action.py @@ -1,3 +1,4 @@ +import json import os import subprocess from dataclasses import dataclass @@ -70,12 +71,21 @@ def _prepare_cache_control_and_content_type_command(cache_config: S3ArtifactCust def _get_default_cache_control(): return f"--cache-control '{config.default_cache_control}'" if config.default_cache_control else "" + metadata = json.dumps( + { + "X-Frame-Options": "SAMEORIGIN", + "Content-Security-Policy": "frame-src 'self'; frame-ancestors 'self'; object-src 'none';", + "Strict-Transport-Security": "max-age=31536000; includeSubDomains", + "X-Content-Type-Options": "nosniff", + }, + ) return ( f"aws s3 sync {config.local_artifacts_path} {target} {_get_default_cache_control()} {S3_SYNC_OPTIONS}", *( _prepare_cache_control_and_content_type_command(custom_metadata) for custom_metadata in config.custom_metadata ), + f"aws s3 cp {target} {target} {S3_CP_OPTIONS} --exclude '*' --include '*.html' --metadata '{metadata}'", ) diff --git a/tests/test_action.py b/tests/test_action.py index a0cba47..31b1270 100644 --- a/tests/test_action.py +++ b/tests/test_action.py @@ -1,3 +1,4 @@ +import json import os from dataclasses import asdict from tempfile import NamedTemporaryFile @@ -29,11 +30,24 @@ def _get_website_config(cache: Sequence[S3ArtifactCustomMetadataConfig] = ()) -> ) -def _get_upload_commands(pattern: str, mime_type: str, max_age: str) -> tuple[str, str]: +def _get_metadata_command(): + return ( + f"aws s3 cp {ARTIFACTS_BUCKET} {ARTIFACTS_BUCKET} --recursive --no-progress --exclude '*' --include '*.html' " + f"--metadata '{json.dumps({ + "X-Frame-Options": "SAMEORIGIN", + "Content-Security-Policy": "frame-src 'self'; frame-ancestors 'self'; object-src 'none';", + "Strict-Transport-Security": "max-age=31536000; includeSubDomains", + "X-Content-Type-Options": "nosniff", + })}'" + ) + + +def _get_upload_commands(pattern: str, mime_type: str, max_age: str) -> tuple[str, str, str]: return ( f"aws s3 sync dist/ {ARTIFACTS_BUCKET} --cache-control 'max-age=60' --delete --no-progress", f"aws s3 cp {ARTIFACTS_BUCKET} {ARTIFACTS_BUCKET} --recursive --no-progress --exclude '*' " f"--include {pattern} --metadata-directive REPLACE --content-type '{mime_type}' --cache-control '{max_age}'", + _get_metadata_command(), ) @@ -42,7 +56,10 @@ def test_upload(self): # Test success without special metadata self.assertEqual( first=upload(config=_get_website_config(), target=ARTIFACTS_BUCKET), - second=(f"aws s3 sync dist/ {ARTIFACTS_BUCKET} --cache-control 'max-age=60' --delete --no-progress",), + second=( + f"aws s3 sync dist/ {ARTIFACTS_BUCKET} --cache-control 'max-age=60' --delete --no-progress", + _get_metadata_command(), + ), ) # Test success with special metadata