Repository: dbt-labs/dbt-core

Issue:
[CT-2953] [Bug] DBT profile could not be rendered because of sequence of special characters in password
### Is this a new bug in dbt-core?

- [X] I believe this is a new bug in dbt-core
- [X] I have searched the existing issues, and I could not find an existing issue for this bug

### Current Behavior

We are using dbt version **1.4.4** (Restricting to v1.4.4 due to Vertica DB adaptor limitation)

So we have our dbt running on a container, and the profiles.yaml is dynamically created by script at bootup time based on a Vertica DB which is also spun up using IaC (and has its db password rotated at compliant intervals)
Today we had an issue where the DBT failed to execute because of failure to load the profiles.yaml due to an issue with the connection password string that was randomly generated.
The connection password happened to be **bN{%UgRk?U!yGe4}ZfQ.IPwD@**
DBT fails with the below log:

```
Profile loading failed for the following reason:
Runtime Error
  Compilation Error
    Could not render "bN{%UgRk?U!yGe4}ZfQ.IPwD@": Encountered unknown tag 'UgRk'.
      line 1
        "bN{%UgRk?U!yGe4}ZfQ.IPwD@"
```
The password does contain special characters and as per the [DBT connection profile doc](https://docs.getdbt.com/docs/core/connect-data-platform/connection-profiles) here i did have it wrapped in quotes. However this issue still persists.
A little bit of googling and I think it has to do with dbt/yaml rendering thinking its a jinja template because of the sequence of "{%" in the password string.


### Expected Behavior

Is there anyway i can resolve this rendering issue? Is there a way to inject the connection string outside of the profiles.yaml file?
(Also it so happens i dont have control over the choice or sequence of special characters that go into the DB random password, so thats a dead end) 

### Steps To Reproduce

1. DBT version 1.4.4 
2. Have dbt profile set up with connection password as  bN{%UgRk?U!yGe4}ZfQ.IPwD@
3. Perform a dbt run or 'dbt debug'

### Relevant log output

```shell
Could not load dbt_project.yml

Profile loading failed for the following reason:
Runtime Error
  Compilation Error
    Could not render bN{%UgRk?U!yGe4}ZfQ.IPwD@: Encountered unknown tag 'UgRk'.
      line 1
        bN{%UgRk?U!yGe4}ZfQ.IPwD@
```


### Environment

```markdown
- OS: Linux-5.10.184-175.749.amzn2.x86_64-x86_64-with
- Python: 3.9.17
- dbt: 1.4.4
```


### Which database adapter are you using with dbt?

other (mention it in "Additional Context")

### Additional Context

Database adaptor: Vertica https://github.com/vertica/dbt-vertica


Discussion:

Resolving patch:
diff --git a/core/dbt/config/renderer.py b/core/dbt/config/renderer.py
--- a/core/dbt/config/renderer.py
+++ b/core/dbt/config/renderer.py
@@ -182,7 +182,17 @@ def render_value(self, value: Any, keypath: Optional[Keypath] = None) -> Any:
         # First, standard Jinja rendering, with special handling for 'secret' environment variables
         # "{{ env_var('DBT_SECRET_ENV_VAR') }}" -> "$$$DBT_SECRET_START$$$DBT_SECRET_ENV_{VARIABLE_NAME}$$$DBT_SECRET_END$$$"
         # This prevents Jinja manipulation of secrets via macros/filters that might leak partial/modified values in logs
-        rendered = super().render_value(value, keypath)
+
+        try:
+            rendered = super().render_value(value, keypath)
+        except Exception as ex:
+            if keypath and "password" in keypath:
+                # Passwords sometimes contain jinja-esque characters, but we
+                # don't want to render them if they aren't valid jinja.
+                rendered = value
+            else:
+                raise ex
+
         # Now, detect instances of the placeholder value ($$$DBT_SECRET_START...DBT_SECRET_END$$$)
         # and replace them with the actual secret value
         if SECRET_ENV_PREFIX in str(rendered):


Test patch:
diff --git a/tests/functional/profiles/test_profiles_yml.py b/tests/functional/profiles/test_profiles_yml.py
new file mode 100644
--- /dev/null
+++ b/tests/functional/profiles/test_profiles_yml.py
@@ -0,0 +1,64 @@
+import pathlib
+from test_profile_dir import environ
+
+from dbt.cli.main import dbtRunner
+
+jinjaesque_password = "no{{jinja{%re{#ndering"
+
+profile_with_jinjaesque_password = f"""test:
+  outputs:
+    default:
+      dbname: my_db
+      host: localhost
+      password: {jinjaesque_password}
+      port: 12345
+      schema: dummy
+      threads: 4
+      type: postgres
+      user: peter.webb
+  target: default
+"""
+
+profile_with_env_password = """test:
+  outputs:
+    default:
+      dbname: my_db
+      host: localhost
+      password: "{{ env_var('DBT_PASSWORD') }}"
+      port: 12345
+      schema: dummy
+      threads: 4
+      type: postgres
+      user: peter.webb
+  target: default
+"""
+
+
+class TestProfileParsing:
+    def write_profiles_yml(self, profiles_root, content) -> None:
+        with open(pathlib.Path(profiles_root, "profiles.yml"), "w") as profiles_yml:
+            profiles_yml.write(content)
+
+    def test_password_not_jinja_rendered_when_invalid(self, project, profiles_root) -> None:
+        """Verifies that passwords that contain Jinja control characters, but which are
+        not valid Jinja, do not cause errors."""
+        self.write_profiles_yml(profiles_root, profile_with_jinjaesque_password)
+
+        events = []
+        result = dbtRunner(callbacks=[events.append]).invoke(["parse"])
+        assert result.success
+
+        for e in events:
+            assert "no{{jinja{%re{#ndering" not in e.info.msg
+
+    def test_password_jinja_rendered_when_valid(self, project, profiles_root) -> None:
+        """Verifies that a password value that is valid Jinja is rendered as such,
+        and that it doesn't cause problems if the resulting value looks like Jinja"""
+        self.write_profiles_yml(profiles_root, profile_with_env_password)
+
+        events = []
+        with environ({"DBT_PASSWORD": jinjaesque_password}):
+            result = dbtRunner(callbacks=[events.append]).invoke(["parse"])
+
+        assert result.success
+        assert project.adapter.config.credentials.password == jinjaesque_password