Skip to content

Commit

Permalink
Merge branch '519-ignore-terraform-version-from-tfsec-in-analytics' i…
Browse files Browse the repository at this point in the history
…nto 'main'

fix: Ensure user-agent matches expected Terraform or OpenTofu user agents

Closes #519

See merge request pub/terrareg!404
  • Loading branch information
MatthewJohn committed May 26, 2024
2 parents 42859a5 + 7eea51d commit 7b3cc0a
Show file tree
Hide file tree
Showing 4 changed files with 106 additions and 14 deletions.
24 changes: 12 additions & 12 deletions terrareg/analytics.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

import re
import datetime
from typing import Union, List
from typing import Union, List, Optional

import sqlalchemy

Expand Down Expand Up @@ -70,7 +70,7 @@ def _join_filter_analytics_table_by_module_provider(db, query, module_provider):
)

@staticmethod
def get_environment_from_token(auth_token):
def get_environment_from_token(auth_token: Optional[str]):
"""Check if auth token matches required environment analytics tokens."""
# If no analytics tokens have been defined, return default environment
if not AnalyticsEngine.are_tokens_enabled():
Expand Down Expand Up @@ -98,18 +98,18 @@ def record_module_version_download(
module_name: str,
provider_name: str,
module_version,
analytics_token: str,
terraform_version: str,
user_agent: str,
auth_token: str):
analytics_token: Optional[str],
terraform_version: Optional[str],
user_agent: Optional[str],
auth_token: Optional[str]):
"""Store information about module version download in database."""

# If Terraform version not present from header,
# attempt to determine from user agent
if not terraform_version:
user_agent_match = re.match(r'^Terraform/(\d+\.\d+\.\d+)$', user_agent)
if user_agent_match:
terraform_version = user_agent_match.group(1)
# Use the X-Terraform-Version header, if the user agent matches an allowed
# list of user agents.
# If the user agent does not match any of the expected prefixes, do not
# record the terraform version.
if (not user_agent) or (not any([user_agent.startswith(prefix) for prefix in ['Terraform/', 'OpenTofu/']])):
terraform_version = None

# Obtain environment from auth token.
# If auth token is not provided,
Expand Down
3 changes: 2 additions & 1 deletion test/integration/terrareg/analytics_engine/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,4 +129,5 @@ def _import_test_analytics(self, download_data):
AnalyticsEngine.record_module_version_download(
namespace_name=namespace, module_name=module, provider_name=provider,
module_version=module_version, terraform_version=terraform_version,
analytics_token=analytics_token, user_agent=None, auth_token=auth_token)
analytics_token=analytics_token, user_agent="Terraform/{}".format(terraform_version),
auth_token=auth_token)
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ def _create_analytics(self, namespace, module, provider, version, token, timesta
module_version=version,
analytics_token=token,
terraform_version='1.1.1',
user_agent='',
user_agent='Terraform/1.1.1',
auth_token=None
)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@

import pytest

import terrareg.models
from terrareg.analytics import AnalyticsEngine
from . import AnalyticsIntegrationTest


class TestRecordModuleVersionDownload(AnalyticsIntegrationTest):
"""Test record_module_version_download function."""

_TEST_ANALYTICS_DATA = {}

@pytest.mark.parametrize("user_agent, terraform_version", [
# No user agent
(None, "1.5.3"),
# No version
("Terraform/1.5.2", None),
# Invalid user-agent
("Go-http-client/1.1", "1.5.3"),
])
def test_ignore_terraform_version(self, user_agent, terraform_version):
"""Test function with ignoring terraform version headers."""
namespace = "testnamespace"
module = "publishedmodule"
provider = "testprovider"
analytics_token = "test-invalid-version-headers"

namespace_obj = terrareg.models.Namespace.get(namespace)
module_obj = terrareg.models.Module(namespace_obj, module)
provider_obj = terrareg.models.ModuleProvider.get(module_obj, provider)
version_obj = terrareg.models.ModuleVersion.get(provider_obj, "1.4.0")

# Clean up any analytics
AnalyticsEngine.delete_analytics_for_module_version(version_obj)

AnalyticsEngine.record_module_version_download(
namespace_name=namespace, module_name=module, provider_name=provider,
module_version=version_obj, terraform_version=terraform_version,
analytics_token=analytics_token, user_agent=user_agent,
auth_token=None
)

results = AnalyticsEngine.get_module_provider_token_versions(provider_obj)
assert results == {
'test-invalid-version-headers': {
'environment': 'Default',
'module_version': '1.4.0',
'terraform_version': '0.0.0'
}
}

@pytest.mark.parametrize("user_agent, terraform_version", [
# Terraform
("Terraform/1.5.0", "1.5.3"),
# OpenTofu
("OpenTofu/1.5.2", "1.5.3"),
])
def test_valid_terraform_version(self, user_agent, terraform_version):
"""Test function with ignoring terraform version headers."""
namespace = "testnamespace"
module = "publishedmodule"
provider = "testprovider"
analytics_token = "test-with-version"

namespace_obj = terrareg.models.Namespace.get(namespace)
module_obj = terrareg.models.Module(namespace_obj, module)
provider_obj = terrareg.models.ModuleProvider.get(module_obj, provider)
version_obj = terrareg.models.ModuleVersion.get(provider_obj, "1.4.0")

# Clean up any analytics
AnalyticsEngine.delete_analytics_for_module_version(version_obj)

AnalyticsEngine.record_module_version_download(
namespace_name=namespace, module_name=module, provider_name=provider,
module_version=version_obj, terraform_version=terraform_version,
analytics_token=analytics_token, user_agent=user_agent,
auth_token=None
)

results = AnalyticsEngine.get_module_provider_token_versions(provider_obj)
assert results == {
'test-with-version': {
'environment': 'Default',
'module_version': '1.4.0',
'terraform_version': '1.5.3'
}
}

0 comments on commit 7b3cc0a

Please sign in to comment.