Repository: PrefectHQ/prefect

Issue:
Secret is partially visible
### First check

- [X] I added a descriptive title to this issue.
- [X] I used the GitHub search to find a similar issue and didn't find it.
- [X] I refreshed the page and this issue still occurred.
- [X] I checked if this issue was specific to the browser I was using by testing with a different browser.

### Bug summary

I used Secret Block on Prefect Cloud to store a secret, and its value is partially visible.

### Reproduction

1. Sign in to Prefect Cloud
2. Click "Blocks" from side menu
3. Click "+" button in order to add a new Block
4. Click "Add +" button under "Secret"
5. Write Block name
6. Write Value

### Error

![image](https://user-images.githubusercontent.com/83173240/188316397-1ae63fcc-0102-43c0-bb05-4398d53520bf.png)

### Browers

- [X] Chrome
- [X] Firefox
- [ ] Safari
- [ ] Edge

### Prefect version

_No response_

### Additional context

_No response_


Discussion:
Hi! For long secrets we include a small portion of the value to help with identification.
I concur with [emkademy](https://github.com/emkademy).

This is really bad for multiple reasons.

1. Secrets are secret. I don't need help with remembering what they're set to but I do need them to stay secret.
2. Why does the UI have permission to load a decrypted secret at all?
3. This is a problem for several flavours of audit compliance.

As-is, the standard Secret block is unusable.

Also potentially worrying:

The documentation on creating new block types [advises to use SecretStr to prevent displaying values in the UI](https://docs.prefect.io/concepts/blocks/#secret-fields). It’s not clear to me if the same issue of partial secret disclosure will arise for custom blocks using SecretStr values…

Question: For custom blocks using SecretStr:
1. Will these values ever display even partially in the UI or in logs?
2. Does the UI code have unencrypted access to these values?

> Why does the UI have permission to load a decrypted secret at all?

This is not a feature implemented at the UI layer. This representation is returned by the API and is implemented for all block documents (which includes custom blocks). See the open source implementation in [orion/schemas/core.py](https://github.com/PrefectHQ/prefect/blob/0c14eefc6b1a4bf438035277c5b8a5b322beddde/src/prefect/orion/schemas/core.py#L577-L592).

The UI is running in your browser and it is making requests with the permissions of your account. The UI code isn't running on Prefect's servers per-say. @abrookins and @cicdw  can address any further questions on the security aspects of this.

@billpalombi leads our product team and will use the feedback here and from other channels to decide if we want to keep this behavior, remove it, or make it configurable per workspace.

Original change in https://github.com/PrefectHQ/orion/pull/2359

Resolving patch:
diff --git a/src/prefect/orion/utilities/names.py b/src/prefect/orion/utilities/names.py
--- a/src/prefect/orion/utilities/names.py
+++ b/src/prefect/orion/utilities/names.py
@@ -40,12 +40,12 @@ def generate_slug(n_words: int) -> str:
     return "-".join(words)
 
 
-def obfuscate_string(s: str) -> str:
+def obfuscate_string(s: str, show_tail=False) -> str:
     """
     Obfuscates a string by returning a new string of 8 characters. If the input
-    string is longer than 10 characters, then up to 4 of its final characters
-    will become final characters of the obfuscated string; all other characters
-    are "*".
+    string is longer than 10 characters and show_tail is True, then up to 4 of
+    its final characters will become final characters of the obfuscated string;
+    all other characters are "*".
 
     "abc"      -> "********"
     "abcdefgh" -> "********"
@@ -55,6 +55,6 @@ def obfuscate_string(s: str) -> str:
     result = OBFUSCATED_PREFIX + "*" * 4
     # take up to 4 characters, but only after the 10th character
     suffix = s[10:][-4:]
-    if suffix:
+    if suffix and show_tail:
         result = f"{result[:-len(suffix)]}{suffix}"
     return result


Test patch:
diff --git a/tests/orion/api/test_block_documents.py b/tests/orion/api/test_block_documents.py
--- a/tests/orion/api/test_block_documents.py
+++ b/tests/orion/api/test_block_documents.py
@@ -1035,6 +1035,9 @@ async def test_create_secret_block_document_obfuscates_results(
         assert block.data["y"] == obfuscate_string(Y)
         assert block.data["z"] == Z
 
+        # by default, no characters of secrets are shown
+        assert block.data["y"] == "*" * 8
+
     async def test_read_secret_block_document_by_id_obfuscates_results(
         self, client, secret_block_document
     ):
diff --git a/tests/orion/utilities/test_names.py b/tests/orion/utilities/test_names.py
--- a/tests/orion/utilities/test_names.py
+++ b/tests/orion/utilities/test_names.py
@@ -20,4 +20,7 @@
     ],
 )
 def test_obfuscate_string(s, expected):
-    assert obfuscate_string(s) == expected
+    # default is not to reveal any characters
+    assert obfuscate_string(s) == obfuscate_string(s, show_tail=False) == "*" * 8
+    # show tail
+    assert obfuscate_string(s, show_tail=True) == expected