-
Notifications
You must be signed in to change notification settings - Fork 5
feat: treat namespaces as a special case when checking for typos #378
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1 +0,0 @@ | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,6 @@ | ||
| from twyn.trusted_packages.managers.trusted_npm_packages_manager import TrustedNpmPackageManager | ||
| from twyn.trusted_packages.managers.trusted_pypi_packages_manager import TrustedPackages | ||
| from twyn.trusted_packages.references.top_npm_reference import TopNpmReference | ||
| from twyn.trusted_packages.references.top_pypi_reference import TopPyPiReference | ||
| from twyn.trusted_packages.trusted_packages import TrustedPackages | ||
|
|
||
| __all__ = ["TopPyPiReference", "TrustedPackages", "TopNpmReference"] | ||
| __all__ = ["TopPyPiReference", "TopNpmReference", "TrustedPackages", "TrustedNpmPackageManager"] |
Empty file.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| from collections import defaultdict | ||
| from typing import Any, Protocol | ||
|
|
||
| from twyn.trusted_packages.models import TyposquatCheckResultEntry | ||
|
|
||
| OrderedPackages = defaultdict[str, set[str]] | ||
| """Type alias for mapping package names by ecosystem.""" | ||
|
|
||
|
|
||
| class TrustedPackagesProtocol(Protocol): | ||
| def __contains__(self, obj: Any) -> bool: ... | ||
|
|
||
| def get_typosquat(self, package_name: str) -> TyposquatCheckResultEntry: ... |
77 changes: 77 additions & 0 deletions
77
src/twyn/trusted_packages/managers/trusted_npm_packages_manager.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,77 @@ | ||
| from collections import defaultdict | ||
| from typing import Any | ||
|
|
||
| from twyn.similarity.algorithm import ( | ||
| AbstractSimilarityAlgorithm, | ||
| SimilarityThreshold, | ||
| ) | ||
| from twyn.trusted_packages.managers.base import OrderedPackages | ||
| from twyn.trusted_packages.models import TyposquatCheckResultEntry | ||
| from twyn.trusted_packages.selectors import AbstractSelector | ||
|
|
||
|
|
||
| class TrustedNpmPackageManager: | ||
| """Representation of namespaces that can be trusted.""" | ||
|
|
||
| def __init__( | ||
| self, | ||
| names: set[str], | ||
| algorithm: AbstractSimilarityAlgorithm, | ||
| selector: AbstractSelector, | ||
| threshold_class: type[SimilarityThreshold], | ||
| ) -> None: | ||
| self.packages, self.namespaces = self._create_names_dictionary(names) | ||
|
|
||
| self.threshold_class = threshold_class | ||
| self.selector = selector | ||
| self.algorithm = algorithm | ||
|
|
||
| def __contains__(self, obj: Any) -> bool: | ||
| """Check if an object exists in the trusted namespaces.""" | ||
| if isinstance(obj, str): | ||
| return obj in self.packages[obj[0]] or obj in self.namespaces | ||
| return False | ||
|
|
||
| def _create_names_dictionary(self, names: set[str]) -> tuple[OrderedPackages, OrderedPackages]: | ||
| """Create a dictionary which will group all packages that start with the same letter under the same key.""" | ||
| first_letter_names: OrderedPackages = defaultdict(set) | ||
sdn4z marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| namespaces: OrderedPackages = defaultdict(set) | ||
| for name in names: | ||
| if name.startswith("@"): | ||
| namespace, dependency = name.split("/") | ||
| namespaces[namespace].add(dependency) | ||
| else: | ||
| first_letter_names[name[0]].add(name) | ||
| return first_letter_names, namespaces | ||
|
|
||
| def _get_typosquats_from_namespace_dependency(self, package_name: str) -> TyposquatCheckResultEntry: | ||
| namespace, dependency = package_name.split("/") | ||
| threshold = self.threshold_class.from_name(namespace) | ||
| typosquat_result = TyposquatCheckResultEntry(dependency=package_name) | ||
| for trusted_namespace_name in self.selector.select_similar_names( | ||
| names={"@": self.namespaces.keys()}, name=namespace | ||
| ): | ||
| distance = self.algorithm.get_distance(namespace, trusted_namespace_name) | ||
| if threshold.is_inside_threshold(distance) and dependency in self.namespaces[trusted_namespace_name]: | ||
| typosquat_result.add(f"{trusted_namespace_name}/{dependency}") | ||
| return typosquat_result | ||
|
|
||
| def _get_typosquats_from_dependency(self, package_name: str) -> TyposquatCheckResultEntry: | ||
| threshold = self.threshold_class.from_name(package_name) | ||
| typosquat_result = TyposquatCheckResultEntry(dependency=package_name) | ||
| for trusted_package_name in self.selector.select_similar_names(names=self.packages, name=package_name): | ||
| distance = self.algorithm.get_distance(package_name, trusted_package_name) | ||
| if threshold.is_inside_threshold(distance): | ||
| typosquat_result.add(trusted_package_name) | ||
| return typosquat_result | ||
|
|
||
| def get_typosquat(self, package_name: str) -> TyposquatCheckResultEntry: | ||
| """Check if a given package name is similar to any trusted package and returns it. | ||
|
|
||
| Only if there is a match on the first letter can a package name be | ||
| considered similar to another one. The algorithm provided and the threshold | ||
| are used to determine if the package name can be considered similar. | ||
| """ | ||
| if package_name.startswith("@"): | ||
| return self._get_typosquats_from_namespace_dependency(package_name) | ||
| return self._get_typosquats_from_dependency(package_name) | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.