Skip to content

Fix secure credential loading for nested team-config profiles - #411

Open
ATorrise wants to merge 3 commits into
mainfrom
fix/nested-secure-profile-properties
Open

Fix secure credential loading for nested team-config profiles#411
ATorrise wants to merge 3 commits into
mainfrom
fix/nested-secure-profile-properties

Conversation

@ATorrise

@ATorrise ATorrise commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

What It Does
When testing #408 i found that the way i structured my config was returning 401s
ie:

mainframe ->
  -> properties
       -> host
  -> profiles
       -> zosmf ->
            -> properties
                 -> port
            -> secure
                 -> user
                 -> password

found that __load_secure_properties looked for zosmf as a direct key of the mainframe profile but zosmf actually lives one level deeper, under the profiles key.

the fix to config_file.py handles however many levels a config nests

test_custom_file_and_custom_profile_loading_with_nested_profile user/password values came from a plaintext override in nested.zowe.config.user.json, not from the vault. So apparently no test ever exercised __load_secure_properties for a profile nested more than one level deep with secure fields on the child! Added a new test (test_nested_profile_with_secure_properties_on_child) that pulls creds from the mocked vault
How to Test
change your config to match this level of nesting when testing. verify that you get 401s on main and you can run system tests on this branch :)
Review Checklist
I certify that I have:

  • updated the changelog
  • manually tested my changes
  • added/updated automated unit/integration tests
  • created/ran system tests (provide build number if applicable)
  • followed the contribution guidelines

Additional Comments

Signed-off-by: ATorrise <ambertorrise@gmail.com>
Signed-off-by: ATorrise <ambertorrise@gmail.com>
@github-project-automation github-project-automation Bot moved this to New Issues in Zowe CLI Squad Aug 7, 2026
@zowe-robot zowe-robot moved this from New Issues to Review/QA in Zowe CLI Squad Aug 7, 2026
@ATorrise
ATorrise requested a review from t1m0thyj August 7, 2026 18:39
@codecov

codecov Bot commented Aug 7, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 83.40%. Comparing base (91f8021) to head (a411a6a).

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #411      +/-   ##
==========================================
+ Coverage   83.06%   83.40%   +0.34%     
==========================================
  Files          49       49              
  Lines        2982     2983       +1     
==========================================
+ Hits         2477     2488      +11     
+ Misses        505      495      -10     
Flag Coverage Δ
unittests 83.40% <100.00%> (+0.34%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Signed-off-by: ATorrise <ambertorrise@gmail.com>

@zFernand0 zFernand0 left a comment

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.

LGTM! 😋

Very good catch! 🙏

Here is a quick before and after of this fix:

Before

{'rejectUnauthorized': False, 'host': 'lpar.1', 'port': 1234}
HTTP Request has failed with status code 401. 

After

{'rejectUnauthorized': False, 'host': 'lpar.1', 'port': 1234, 'user': 'dummy', 'password': 'dummy'}
HTTP Request has failed with status code 401. 

I got the 401 because dummy:dummy is not valid on my system


With proper creds:

{'rejectUnauthorized': False, 'host': 'lpar.1', 'port': 1234, 'user': 'REALUSER', 'password': 'PASSWORD'}
//* //FERNAN00 JOB (IZUACCT),FERNANDO,CLASS=B,MSGCLASS=X,TYPRUN=HOLD
...

@traeok traeok left a comment

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.

I noticed that even with these changes, the failure is never raised from check_missing_props, which is likely why @zFernand0 was seeing a 401 rather than an explicit error. I'd consider it out of scope for this PR, but worth a follow-up issue.

Aside from that, I left a suggestion around the modified code in an attempt to de-duplicate logic.

Comment on lines 448 to +459
for i, profile_name in enumerate(segments):
if profiles_obj is None or not isinstance(profiles_obj, dict):
break
if profile_name in profiles_obj:
profiles_obj = profiles_obj[profile_name]
if not isinstance(profiles_obj, dict):
break
if i == len(segments) - 1:
profiles_obj.setdefault("properties", {})
profiles_obj["properties"][property_name] = value
else:
profiles_obj = profiles_obj.get("profiles", {})

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.

I know the loop is pre-existing, but this walk looks identical to find_profile. Could we just use that instead and remove this whole loop?

For example:

profile = self.find_profile(".".join(segments), self.profiles)
if isinstance(profile, dict):
    profile.setdefault("properties", {})[property_name] = value

@zFernand0 zFernand0 self-assigned this Aug 10, 2026
@awharn
awharn requested a review from zFernand0 August 18, 2026 19:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Review/QA

Development

Successfully merging this pull request may close these issues.

4 participants