Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/poetry/packages/locker.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ def __init__(self, lock: Path, local_config: dict[str, Any]) -> None:
self._local_config = local_config
self._lock_data: dict[str, Any] | None = None
self._content_hash = self._get_content_hash()
self._contains_credential = False

@property
def lock(self) -> Path:
Expand Down Expand Up @@ -285,6 +286,10 @@ def _should_write(self, lock: TOMLDocument) -> bool:
return do_write

def _write_lock_data(self, data: TOMLDocument) -> None:
if self._contains_credential:
logger.warning(
"A package URL contains credentials and will be written to poetry.lock."

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.

This looks like a frustrating error message - I might plausibly have hundreds of packages in poetry.lock, being told that one of them has credentials is going to leave me with work to do...

Can this warning say which package is the problematic one?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

The warning is not triggered by a package. It is displayed if credentials are in the URL of an added source in package.source section.

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.

Those credentials are associated with a particular package, no? eg in your testcase, it's package A that triggers the warning.

If I have a lockfile with hundreds of packages in it, then a more helpful warning would point me at A rather than only telling me that there's a problem, somewhere.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

When I got this issue, the URL coming from the package.source was added to every packages in the lock file. So it's not useful. However, adding the URL in the message could help the user:

Package URL contain credentials (https://alice:a_passwd@pypi.domain.tld/site/prod/+simple) and will be written to poetry.lock.

From file:

[package.source]
type = "legacy"
url = "https://alice:a_passwd@pypi.domain.tld/site/prod/+simple"
reference = "whatever"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This LGTM, except I think we should resolve this thread. Can we include the URL?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

@neersighted I can modify _contains_credential to be the URL instead of a boolean status. In such case, the name fits less well. If it's still ok fo you, I'll be happy to modify it. An other way would be to rename it but it will also require to rename it in the poetry-plugin-export repository. The third way is to not add the URL. My favorite way is the first one (modifying the content of _contains_credential). What do you prefer?

)
lockfile = TOMLFile(self.lock)
lockfile.write(data)

Expand Down Expand Up @@ -455,6 +460,7 @@ def _dump_package(self, package: Package) -> dict[str, Any]:

if package.source_url:
url = package.source_url
self._contains_credential |= bool(re.match("[a-z]+://.*:.*@.*", url))
if package.source_type in ["file", "directory"]:
# The lock file should only store paths relative to the root project
url = Path(
Expand Down
1 change: 1 addition & 0 deletions tests/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ def __init__(self, lock: Path, local_config: dict[str, Any]) -> None:
super().__init__(lock, local_config)
self._locked = False
self._write = False
self._contains_credential = False

def write(self, write: bool = True) -> None:
self._write = write
Expand Down
1 change: 1 addition & 0 deletions tests/installation/test_installer.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ def __init__(self, lock_path: Path) -> None:
self._locked = False
self._lock_data = None
self._content_hash = self._get_content_hash()
self._contains_credential = False

@property
def written_data(self) -> dict[str, Any]:
Expand Down
35 changes: 34 additions & 1 deletion tests/packages/test_locker.py
Original file line number Diff line number Diff line change
Expand Up @@ -1054,12 +1054,13 @@ def test_locked_repository_uses_root_dir_of_package(
],
)
def test_content_hash_with_legacy_is_compatible(
local_config: dict[str, list[str]], fresh: bool, locker: Locker
local_config: dict[str, list[str]], fresh: bool, locker: Locker, caplog: LogCaptureFixture
) -> None:
# old hash generation
relevant_content = {}
for key in locker._legacy_keys:
relevant_content[key] = local_config.get(key)
caplog.set_level(logging.WARNING, logger="poetry.packages.locker")

locker = locker.__class__(
lock=locker.lock,
Expand Down Expand Up @@ -1185,3 +1186,35 @@ def test_lockfile_is_not_rewritten_if_only_poetry_version_changed(
content = f.read()

assert content == old_content


def test_credentials_in_lock_file(
locker: Locker, root: ProjectPackage
) -> None:
package = Package(
"A",
"1.0.0",
source_type="legacy",
source_url="https://user:password@foo.bar",
source_reference="legacy",
)

locker.set_lock_data(root, [package])

assert locker._contains_credential is True


def test_no_credentials_in_lock_file(
locker: Locker, root: ProjectPackage
) -> None:
package = Package(
"A",
"1.0.0",
source_type="legacy",
source_url="https://foo.bar",
source_reference="legacy",
)

locker.set_lock_data(root, [package])

assert locker._contains_credential is False