Skip to content

Fix bugs, security issues, and reliability problems#146

Open
stephen-bailey-carta wants to merge 1 commit intogrokability:mainfrom
stephen-bailey-carta:fix/bugs-security-reliability
Open

Fix bugs, security issues, and reliability problems#146
stephen-bailey-carta wants to merge 1 commit intogrokability:mainfrom
stephen-bailey-carta:fix/bugs-security-reliability

Conversation

@stephen-bailey-carta
Copy link
Copy Markdown

Summary

Targeted fixes for bugs, security issues, and reliability problems — no structural changes.

Critical bugs fixed

  • search_jamf_mobile called search_jamf_asset on rate-limit retry (line 416), returning computer data instead of mobile device data
  • get_snipe_models checked response.status_code instead of newresponse.status_code after fetching the second page of models, so failures on the second request were never caught
  • get_snipe_users used mutable default argument (previous=[]), a classic Python gotcha where the default list persists across calls and leaks data
  • logging.verbose(jamf) crashes at runtime — Python's logging module has no verbose level; changed to logging.debug

Security fixes

  • XML injection in update_jamf_asset_tag / update_jamf_mobiledevice_asset_tag — asset tags containing <, >, or & could break or exploit XML payloads; now escaped via xml.sax.saxutils.escape
  • Bare except: clauses replaced with specific exception types (KeyError, ValueError, TypeError, etc.) throughout — bare excepts catch KeyboardInterrupt and SystemExit, masking real errors
  • Snipe API key no longer logged in debug output

Reliability fixes

  • Added 30-second timeout to all 21+ HTTP requests — without timeouts, a network hang blocks the script indefinitely
  • Removed id builtin shadowing in get_snipe_user_id

Test plan

  • Verify syntax: python3 -c "import py_compile; py_compile.compile('jamf2snipe', doraise=True)"
  • Run with --dryrun to verify no behavioral changes in normal flow
  • Test mobile device sync to verify the search_jamf_mobile fix
  • Verify model pagination works when total > initial page size

🤖 Generated with Claude Code

**Critical bugs fixed:**
- `search_jamf_mobile` called `search_jamf_asset` on rate-limit retry, returning computer data instead of mobile device data
- `get_snipe_models` checked `response.status_code` instead of `newresponse.status_code` after second page fetch
- `get_snipe_users` used mutable default argument (`previous=[]`), causing data leakage across calls
- `logging.verbose(jamf)` crashes at runtime — `verbose` is not a valid logging level

**Security fixes:**
- XML injection in `update_jamf_asset_tag` and `update_jamf_mobiledevice_asset_tag` — asset tags are now escaped via `xml.sax.saxutils.escape`
- Bare `except:` clauses replaced with specific exception types throughout
- Snipe API key no longer logged in debug output

**Reliability fixes:**
- Added 30-second timeout to all HTTP requests (21 calls) to prevent indefinite hangs
- Removed `id` builtin shadowing in `get_snipe_user_id`

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown
Collaborator

@ParadoxGuitarist ParadoxGuitarist left a comment

Choose a reason for hiding this comment

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

Hey, thanks for opening a PR! :)

Just to clarify, I don't work for Snipe or anything, but there's some changes I would make and some things @uberbrady or @snipe might need to weigh in on.

Comment thread jamf2snipe
Comment on lines 148 to +157
logging.debug("The client_secret you provided for Jamf is: {}".format(jamf_client_secret))

# This is the address, cname, or FQDN for your snipe-it instance.
logging.info("Setting the base URL for SnipeIT.")
snipe_base = config['snipe-it']['url']
logging.debug("The configured Snipe-IT base url is: {}".format(snipe_base))

logging.info("Setting the API key for SnipeIT.")
snipe_apiKey = config['snipe-it']['apikey']
logging.debug("The API key you provided for Snipe is: {}".format(snipe_apiKey))
logging.debug("Snipe API key is set (not logging for security).")
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I'm a little confused about what drives part of the change here.

The debug level only runs when it's called so we're not logging these things by default and it's the MOST verbose out of any of the logging levels. Personally I think it's useful as a double idiot check to make sure:

  1. We're using the right config.
  2. That config has the right key.

Maybe there's something I don't know about python logging where that gets run either way? I don't think it does, and running it in debug mode add thousands of lines (or even hundreds of thousands depending on org size). Either way, the Jamf client secret is listed right above here , but we're not removing it there?

Personally I would revert line R157, but if we don't I think we'd need to remove the key from R144 for consistency/concerns.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I was actually thinking about this after I posted and I realized that I think debug mode dumps all the request (including the headers) So all keys are getting exposed there (even if they're base64 encoded) so trying to remove is mostly a fruitless venture, but maybe there's a different side to it. I'd trust @snipe or @uberbrady's opinions a lot more than mine when it comes to security. :)

Comment thread jamf2snipe
# A list of valid subsets are:
version = "1.0.7"

DEFAULT_TIMEOUT = 30
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Would it be possible to change this?

Ideally we should pass this in either from the setting file, or as a runtime variable.

I think you could stick this down in the variable section (R70 or so)

runtimeargs.add_argument("-t", "--timeout", type=int, default=10, help="Sets the DEFAULT_TIMEOUT in seconds for API calls. Expects an integer.")

Last thing, is 30 seconds still too long? It seems like a really long time if the script continues considering it's probably making thousands of calls?

Comment thread jamf2snipe
@@ -32,6 +32,8 @@
# A list of valid subsets are:
version = "1.0.7"
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

nitpick: should probably bump this to 1.1.0

Comment thread jamf2snipe
logging.error("Could not generate an asset tag for this device. Skipping")
# Dump the object for debugging.
logging.verbose(jamf)
logging.debug(jamf)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Since all the payloads get dumped in debug mode, I'd leave this as verbose.

We're just adding more noise in debug mode like this and making verbose mode less useful for an in between step.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants