Help
RSS
API
Feed
Maltego
Contact
Domain > seankerr.dev
×
More information on this domain is in
AlienVault OTX
Is this malicious?
Yes
No
DNS Resolutions
Date
IP Address
2025-01-05
18.238.192.10
(
ClassC
)
2026-02-25
104.21.3.205
(
ClassC
)
Port 443
HTTP/1.1 200 OKDate: Wed, 25 Feb 2026 06:46:45 GMTContent-Type: text/htmlTransfer-Encoding: chunkedConnection: keep-aliveServer: cloudflareLast-Modified: Fri, 21 Nov 2025 14:36:46 GMTNel: {report_to:cf-nel,success_fraction:0.0,max_age:604800}Report-To: {group:cf-nel,max_age:604800,endpoints:{url:https://a.nel.cloudflare.com/report/v4?so7POMUC9en3f2uD%2Ba%2FoMc8zIi6laRjJDhVBspXM9AbwTfyW620N1JejXCH3v2AAIOTNbbB4p2p2Goz0YkLJ6XCz1YMq4GRWsUHk%3D}}Accept-Ranges: bytescf-cache-status: DYNAMICCF-RAY: 9d353f54e941ef2c-PDXalt-svc: h3:443; ma86400 !DOCTYPE html>html langen> head>meta charsetUTF-8>meta namedescription contentThese are my experiences in life and the past 2 decades of software development.>meta namekeywords contenttailwind, tailwind css, tailwindcss, shopify, clickfunnels, click funnels, python, python celery, celery, celery python, celery-sqlalchemy, sqlalchemy, celery sqlalchemy, sqlalchemy celery, postgresql-lock, rabbitmq, carthero, cart hero, apptrends, app trends, saas>meta nameviewport contentwidthdevice-width>link relicon typeimage/png href/favicon.png>title>seankerr.dev/title>link relstylesheet href/_astro/about.14c4bed1.css />link relstylesheet href/_astro/about.02d04f1f.css />style>sectiondata-astro-cid-gvpn4u4b{max-width:810px}/style>/head> body> header classflex items-center py-2 lg:fixed lg:left-0 lg:top-0 lg:overflow-hidden lg:w-1/2 lg:h-full> section classflex flex-col mx-auto p-4 text-center lg:p-0 lg:text-left> h1 class-ms-1 font-logo text-accent-light text-xl/tight lg:-ms-1 lg:text-xl/tight xl:text-3xl/tight 2xl:-ms-2 2xl:text-4xl/tight> a href/>>seankerr.dev/a> /h1> ul classfont-header text-accent-medium text-md lg:text-lg xl:text-2xl> li classinline>Software/li> li classinline>Startups/li> li classinline>Life/li> /ul> p classmt-4 text-white text-sm lg:max-w-255px xl:max-w-380px 2xl:max-w-455px>These are my experiences in life and the past 2 decades of software development./p> nav classmt-4 text-center lg:text-left> ul> li classinline> a classtext-gradient-secondary href/>home/a> /li> li classinline ms-8> a classtext-gradient-secondary href/about/>about/a> /li> li classinline ms-8> a classtext-gradient-secondary hrefhttps://github.com/seankerr>github/a> /li> li classinline ms-8> a classtext-gradient-secondary href/sean_kerr_sr_full_stack_engineer_resume.pdf>resume/a> /li> li classinline ms-8> a classtext-gradient-secondary href/contact/>contact/a> /li> li classinline ms-8> a classtext-gradient-secondary href/credits/>credits/a> /li> /ul> /nav> /section> /header> main> section classmt-12 px-8 lg:relative lg:ml-auto lg:w-1/2> div> span classtext-gradient-tertiary whitespace-nowrap lg:text-gradient-secondary lg:absolute lg:right-full lg:top-1em lg:text-xs> Aug 26, 2024span classhidden pl-2 lg:inline>—/span> /span> h1 classfont-header font-bold py-1 text-accent-dark text-xl lg:text-2xl> a href/posts/serializing-celery-tasks-with-orjson>Serializing Celery tasks with orjson/a> /h1> /div> p>I came across a hrefhttps://github.com/celery/kombu/issues/2027>this Celery GitHub issue/a>today. The OP was wondering if it’s currently possible to configure Celery with supportfor a hrefhttps://github.com/ijl/orjson>orjson/a> serializer./p>p>As it turns out, it is not possible out of the box. And the reasoning provided is toprevent yet another dependency from being added to the already growing list of Celerydependencies./p>p>The OP is posting the following reasons for wanting orjson support:/p>ul>li>The default Python JSON module is slow/li>li>It has no support for dataclass, datetime, numpy, UUID types/li>/ul>p>Both of these issues have affected my Celery implementations as well. Less of an issuedue to performance, because last I checked I am already using Python. 😂/p>p>But moreso due to the missing support for datetime and UUID. These are quite commonconsiderations in just about every Python codebase these days./p>p>All jokes aside, I thought I’d share a working example of orjson serialization inCelery. Cheers!/p>pre classastro-code github-dark stylebackground-color:#24292e;color:#e1e4e8; overflow-x: auto; tabindex0>code>span classline>span stylecolor:#6A737D># system imports/span>/span>span classline>span stylecolor:#F97583>from/span>span stylecolor:#E1E4E8> typing /span>span stylecolor:#F97583>import/span>span stylecolor:#E1E4E8> Any/span>/span>span classline>/span>span classline>span stylecolor:#6A737D># dependency imports/span>/span>span classline>span stylecolor:#F97583>from/span>span stylecolor:#E1E4E8> celery /span>span stylecolor:#F97583>import/span>span stylecolor:#E1E4E8> Celery/span>/span>span classline>span stylecolor:#F97583>from/span>span stylecolor:#E1E4E8> kombu /span>span stylecolor:#F97583>import/span>span stylecolor:#E1E4E8> serialization/span>/span>span classline>/span>span classline>span stylecolor:#F97583>import/span>span stylecolor:#E1E4E8> orjson/span>/span>span classline>/span>span classline>span stylecolor:#F97583>def/span>span stylecolor:#B392F0> initialize_orjson_serializer/span>span stylecolor:#E1E4E8>(celery: Celery) -> /span>span stylecolor:#79B8FF>None/span>span stylecolor:#E1E4E8>:/span>/span>span classline>span stylecolor:#F97583> def/span>span stylecolor:#B392F0> deserialize/span>span stylecolor:#E1E4E8>(data: /span>span stylecolor:#79B8FF>bytes/span>span stylecolor:#F97583> |/span>span stylecolor:#79B8FF> str/span>span stylecolor:#E1E4E8>) -> tupleAny, Any, Any:/span>/span>span classline>span stylecolor:#F97583> return/span>span stylecolor:#E1E4E8> orjson.loads(data)/span>/span>span classline>/span>span classline>span stylecolor:#F97583> def/span>span stylecolor:#B392F0> serialize/span>span stylecolor:#E1E4E8>(data: Any) -> /span>span stylecolor:#79B8FF>str/span>span stylecolor:#E1E4E8>:/span>/span>span classline>span stylecolor:#F97583> return/span>span stylecolor:#E1E4E8> orjson.dumps(data).decode(/span>span stylecolor:#9ECBFF>utf-8/span>span stylecolor:#E1E4E8>)/span>/span>span classline>/span>span classline>span stylecolor:#6A737D> # accept tasks serialized with the following content types/span>/span>span classline>span stylecolor:#E1E4E8> celery.conf.accept_content /span>span stylecolor:#F97583>/span>span stylecolor:#E1E4E8> /span>span stylecolor:#9ECBFF>json/span>span stylecolor:#E1E4E8>/span>/span>span classline>/span>span classline>span stylecolor:#6A737D> # accept results serialized with the following content types/span>/span>span classline>span stylecolor:#E1E4E8> celery.conf.result_accept_content /span>span stylecolor:#F97583>/span>span stylecolor:#E1E4E8> /span>span stylecolor:#9ECBFF>json/span>span stylecolor:#E1E4E8>/span>/span>span classline>/span>span classline>span stylecolor:#6A737D> # celery will serialize outgoing tasks with this content type/span>/span>span classline>span stylecolor:#E1E4E8> celery.conf.task_serializer /span>span stylecolor:#F97583>/span>span stylecolor:#9ECBFF> json/span>/span>span classline>/span>span classline>span stylecolor:#6A737D> # register json as a content type/span>/span>span classline>span stylecolor:#E1E4E8> serialization.register(/span>/span>span classline>span stylecolor:#9ECBFF> json/span>span stylecolor:#E1E4E8>,/span>/span>span classline>span stylecolor:#E1E4E8> serialize,/span>/span>span classline>span stylecolor:#E1E4E8> deserialize,/span>/span>span classline>span stylecolor:#9ECBFF> json/span>span stylecolor:#E1E4E8>,/span>/span>span classline>span stylecolor:#E1E4E8> )/span>/span>/code>/pre> /section>section classmt-12 px-8 lg:relative lg:ml-auto lg:w-1/2> div> span classtext-gradient-tertiary whitespace-nowrap lg:text-gradient-secondary lg:absolute lg:right-full lg:top-1em lg:text-xs> Aug 26, 2024span classhidden pl-2 lg:inline>—/span> /span> h1 classfont-header font-bold py-1 text-accent-dark text-xl lg:text-2xl> a href/posts/using-celery-with-an-sqs-broker>Using Celery with an SQS broker/a> /h1> /div> p>If you know me, you know that I’ve pretty much marrieda hrefhttps://docs.celeryq.dev/en/stable/getting-started/introduction.html>Celery/a> anda hrefhttps://www.rabbitmq.com/>RabbitMQ/a> togetherever since I discovered them. They work well in unison for large projects where thecost of RabbitMQ is justified. Sometimes it’s easy to forget that Celery supportsother brokers such as a hrefhttps://redis.io/>Redis/a> and a hrefhttps://aws.amazon.com/sqs/>SQS/a>,both of which are excellent choices./p>h2 idreliability>Reliability/h2>p>Most people know what Redis is, but I feel that SQS isn’t so widely known in the Celerycommunity. It stands for Simple Queue Service, and it’s AWS’ undeniably reliable messagequeue. And when I say reliable, I’m doing the SQS name a disservice. Here are theirwords, not mine:/p>blockquote>p>Amazon SQS stores all message queues and messages within a single, highly-availableAWS region with multiple redundant Availability Zones (AZs), so that no singlecomputer, network, or AZ failure can make messages inaccessible./p>/blockquote>h2 idwhy-use-sqs-over-rabbitmq-or-redis>Why use SQS over RabbitMQ or Redis?/h2>p>All that aside, SQS is a great choice as a Celery broker because there are no serversto maintain, no upfront costs, and no reason to ever consider scaling issues becauseit’s practically infinite. You can store unlimited SQS messages (tasks in our case)for 14 days at no cost./p>h2 idmaybe-there-is-a-cost>Maybe there is a cost…/h2>p>The only cost that comes to mind is that you can’t runa hrefhttps://github.com/mher/flower>Flower/a>. Without Flower, you won’t be able to see yourcurrently running celery instances or get task insights. These are mission criticalrequirements for large applications, but are negiotable for smaller ones./p>p>Let’s get down to the nitty gritty…/p>h2 idacks-late>ACKs late/h2>p>My recommendation is to enable thea hrefhttps://docs.celeryq.dev/en/stable/userguide/tasks.html#Task.acks_late>strong>acks_late/strong>/a>Celery configuration setting. By default, with strong>acks_late/strong> disabled, as soon as thetask is received, Celery would delete the message from SQS rendering the other importantfeatures such as visibility timeout, dead letter queues and redrive policies useless./p>h2 idvisibility-timeout>Visibility timeout/h2>p>SQS has the concept of a visibility timeout. This simply means when a message is sentto a consumer, that consumer is the only one that will be able to see the message whilethe visibility timeout is in place. If the consumer doesn’t delete the messagewithin the visibility timeout, inevitably the message will be sent to another consumer./p>p>In Celery terms, this means you need to do one of two things when you process the task:/p>ul>li>Return the task successfully (message is deleted from SQS)/li>li>Make sure all retries for the task complete before the visibility timeout expires/li>/ul>p>This functionality differs from a typical broker installation, wherein a task executesand regardless of the ACK status, the same task will not be delivered to anotherconsumer. ACKing late in that scenario only protects you from losing your task in theevent of a worker failure./p>p>In my recent implementation, I found 600 seconds (10 minutes) to be a good visibilitytimeout. This is enough time to run the task and factors in enough time for all retriesas well as potential HTTP timeouts for outbound tasks. If my task fails all retries,Celery will log the exception. Shortly after, the visibility timeout in SQS will expireand the same exact task will run again. This will repeat until the redrive policy kicksin./p>h2 idredrive-policy-and-dead-letter-queues>Redrive policy and dead letter queues/h2>p>When you configure the redrive policy for an SQS queue, you specify thestrong>maxReceiveCount/strong> setting which tells SQS how many times a message can be consumedbefore it is sent to the policy’s dead letter queue./p>p>If you want SQS to function the same way RabbitMQ and Redis do, you can set thestrong>maxReceiveCount/strong> setting to 1. If your task completes successfully before thevisibility timeout expires, it’ll be deleted from the queue. But if the task does notcomplete successfully, SQS will move it to the dead letter queue when the visibilitytimeout expires. From there you can examine the cause of failure, and possibly movemessages back into a working queue once you’ve corrected the code causing the failure./p>p>There are various reasons why you would want to raise the strong>maxReceiveCount/strong>, but theyare for you to decide./p> /section>section classmt-12 px-8 lg:relative lg:ml-auto lg:w-1/2> div> span classtext-gradient-tertiary whitespace-nowrap lg:text-gradient-secondary lg:absolute lg:right-full lg:top-1em lg:text-xs> Jan 11, 2024span classhidden pl-2 lg:inline>—/span> /span> h1 classfont-header font-bold py-1 text-accent-dark text-xl lg:text-2xl> a href/posts/the-oauth-flows-of-highlevel>The OAuth Flows of HighLevel/a> /h1> /div> div classitalic mt-4>This guest post first appeared in a hrefhttps://www.cbnsndwch.io/posts/the-oauth-flows-of-highlevel target_cbnsndwch_link>cbnsndwch.io/a>/div>img srchttps://github.com/cbnsndwch/www/blob/main/src/app/posts/the-oauth-flows-of-highlevel/cover.jpg?rawtrue>div classtext-center> a hrefhttps://datatracker.ietf.org/doc/html/rfc6749 target_rfc> RFC 6749: The OAuth 2.0 Authorization framework /a>/div>p>HighLevel’s API v2 uses the OAuth 2.0 Authorization Code flow for authentication. Thismeans that in order for your app or integration to make API calls, you’ll need toimplement an OAuth client that can receive Authorzation Codes, exchange them for a pairof em>Access Token/em> + em>Refresh Token/em>, and save them both to persistent storage forcontinued use./p>p>See thea hrefhttps://highlevel.stoplight.io/docs/integrations/a04191c0fabf9-authorization target_authorization>Authorization page in HighLevel’s API docs/a> for a getting-started guide and a list of all the scopes available./p>h2 idunderstanding-the-oauth-flows-for-marketplace-apps>Understanding the OAuth Flows for Marketplace Apps/h2>p>The OAuth Authorization Code flow as implemented by HighLevel presents two variants orflavors, depending on whether you’re building a Public App or a Private App./p>h3 idpublic-marketplace-apps>Public Marketplace Apps/h3>p>For verified, approved, public Marketplace Apps, the HighLevel web app takes care ofmost of the heavy lifting. It provides discovery and a UI for installing apps intoagencies and/or locations. Here’s what the flow looks like:/p>img srchttps://raw.githubusercontent.com/cbnsndwch/www/main/src/app/posts/the-oauth-flows-of-highlevel/ghl-oauth-code-grant-flow-for-public-apps.svg altPublic Marketplace Apps Diagram>h3 idprivate-marketplace-apps>Private Marketplace Apps/h3>p>For private Marketplace Apps — that is, apps that have not been reviewed by HighLeveland will therefore not be listed inside the HighLevel web app — you are responsible forinitiating the OAuth flow on your end. You will need to prepare an Authorization URLcontaining your app’s Client ID and any scopes your app needs, and send the user to it.Here’s what the flow looks like:/p>img srchttps://raw.githubusercontent.com/cbnsndwch/www/main/src/app/posts/the-oauth-flows-of-highlevel/ghl-oauth-code-grant-flow-for-private-apps.svg altPrivate Marketplace Apps Diagram>h2 idoauth-20-faqs>OAuth 2.0 FAQs/h2>p>Highlevel lists a few Frequently Asked Questions (FAQs) on their API docs. I’veclarified the most critical ones below. I will add more as I answer questions in the DevCouncil. Feel free to tag me in there and I’ll do my best to get you an answer./p>h3 idhow-long-are-access-tokens-valid-for>How long are Access Tokens valid for?/h3>p>Access Tokens are valid for a day. After that, you can use the Refresh Token to get anew Access Token which will be valid for another day./p>h3 idhow-long-are-refresh-tokens-valid-for>How long are Refresh Tokens valid for?/h3>p>Refresh Tokens are valid for a year or until they are used once, whichever comes first.When you call the em>/token/em> endpoint with a Refresh Token instead of an AuthorizationCode, that refresh Token will become invalid and you’ll get a new one in the response.Save the new token in your database or storage service in place of the original one./p>h2 idhave-more-questions>Have more questions?/h2>p>Reach out to me on the Dev Council Slack Channel or on Twittera hrefhttps://twitter.com/cbnsndwch target_cbnsndwch_twitter>@cbnsndwch/a>.I’m happy to help!/p> /section>section classmt-12 px-8 lg:relative lg:ml-auto lg:w-1/2> div> span classtext-gradient-tertiary whitespace-nowrap lg:text-gradient-secondary lg:absolute lg:right-full lg:top-1em lg:text-xs> Nov 17, 2023span classhidden pl-2 lg:inline>—/span> /span> h1 classfont-header font-bold py-1 text-accent-dark text-xl lg:text-2xl> a href/posts/decrypting-highlevel-sso-session-using-python>Decrypting HighLevel SSO sessions using Python/a> /h1> /div> p>HighLevel recently rolled out support for single-sign-on access, which allows Agenciesand Accounts to connect seamlessly to an installed marketplace application. Becauseit’s such a new feature, there is no documentation available at this time. Butthankfully for me I stumbled acrossa hrefhttps://www.youtube.com/watch?v3rveQDuVlR0>this video/a> by Sergio Leon which explainsthe entire SSO process from the application developer perspective. But most importantly,it shows how to retrieve the encrypted SSO session from GHL. It also explains how todecrypt it using Javascript (a hrefhttps://github.com/brix/crypto-js>CryptoJS library/a>)./p>p>This is where my luck ran out, because I’m not using Javascript on my backend, I’musing Python./p>p>The first thing I did was gather the encryption details from the video and I realizedthe session was encrypted using AES. That’s all I had to go with. I installeda hrefhttps://www.pycryptodome.org/src/introduction>Pycryptodome/a> and away I went. I couldsafely assume that the session data was base64 encoded, so I decoded it and saw thedata in its raw format. This gave away the clue I needed. The raw data was prefixed withem>Salted__/em>. When working with OpenSSL, this is standard practice. In fact, when you google“Encryption Salted__” you will see endless results talking about decrypting OpenSSLdata. It’s also standard practice to use PBKDF2 as the key derivation function.PBKDF2 uses an iteration of the password and random salt to sha256 hash the password,from which the key and initial value are derived. Sadly, that is why I wasted a daytrying to finish the task./p>p>I made the worst assumption from the get go. I saw the em>Salted__/em> prefix and assumedOpenSSL format. I had to take a step back and find documentation (which doesn’t exist)or find somebody who has first hand experience with this. That person is Sergio Leon,from the video above. He pointed me to a stack overflow post regarding how CryptoJSencrypts AES data, and so I took a look through the CryptoJS source code and noticedsomething I wasn’t expecting…/p>pre classastro-code github-dark stylebackground-color:#24292e;color:#e1e4e8; overflow-x: auto; tabindex0>code>span classline>span stylecolor:#F97583>var/span>span stylecolor:#E1E4E8> key /span>span stylecolor:#F97583>/span>span stylecolor:#E1E4E8> EvpKDF./span>span stylecolor:#B392F0>create/span>span stylecolor:#E1E4E8>({ /span>span stylecolor:#F97583>.../span>span stylecolor:#E1E4E8> })./span>span stylecolor:#B392F0>compute/span>span stylecolor:#E1E4E8>(password, salt);/span>/span>/code>/pre>p>Ahhh! There it was. That doesn’t look like PBKDF2. It’s EVPKDF! That was the turningpoint where I realized I was using the wrong key derivation from the beginning. Iswitched to EVP and instantly my problem was solved./p>p>The crux of the problem is two parts:/p>ul>li>CryptoJS is using the same prefix as OpenSSL when encrypting AES data./li>li>It’s using MD5 instead of SHA256 as a hashing mechanism, and it only does a singleiteration instead of thousands (more secure)./li>/ul>p>These two factors will invariably lead others to make the same assumption I did, whichis why I’m writing this post./p>p>And without further ado, here is a working example of decrypting a HighLevel SSO sessionin Python./p>pre classastro-code github-dark stylebackground-color:#24292e;color:#e1e4e8; overflow-x: auto; tabindex0>code>span classline>span stylecolor:#6A737D># system imports/span>/span>span classline>span stylecolor:#F97583>from/span>span stylecolor:#E1E4E8> base64 /span>span stylecolor:#F97583>import/span>span stylecolor:#E1E4E8> b64decode/span>/span>span classline>span stylecolor:#F97583>from/span>span stylecolor:#E1E4E8> typing /span>span stylecolor:#F97583>import/span>span stylecolor:#E1E4E8> Tuple/span>/span>span classline>/span>span classline>span stylecolor:#6A737D># pycryptodome imports/span>/span>span classline>span stylecolor:#F97583>from/span>span stylecolor:#E1E4E8> Crypto.Cipher /span>span stylecolor:#F97583>import/span>span stylecolor:#79B8FF> AES/span>/span>span classline>span stylecolor:#F97583>from/span>span stylecolor:#E1E4E8> Crypto.Hash /span>span stylecolor:#F97583>import/span>span stylecolor:#79B8FF> MD5/span>/span>span classline>span stylecolor:#F97583>from/span>span stylecolor:#E1E4E8> Crypto.Util.Padding /span>span stylecolor:#F97583>import/span>span stylecolor:#E1E4E8> unpad/span>/span>span classline>/span>span classline>span stylecolor:#6A737D># encryption details/span>/span>span classline>span stylecolor:#79B8FF>BLOCK_SIZE/span>span stylecolor:#F97583> /span>span stylecolor:#79B8FF> AES/span>span stylecolor:#E1E4E8>.block_size/span>/span>span classline>span stylecolor:#79B8FF>KEY_SIZE/span>span stylecolor:#F97583> /span>span stylecolor:#79B8FF> 32/span>/span>span classline>span stylecolor:#79B8FF>IV_SIZE/span>span stylecolor:#F97583> /span>span stylecolor:#79B8FF> 16/span>/span>span classline>span stylecolor:#79B8FF>SALT_SIZE/span>span stylecolor:#F97583> /span>span stylecolor:#79B8FF> 8/span>/span>span classline>/span>span classline>span stylecolor:#6A737D># working data/span>/span>span classline>span stylecolor:#79B8FF>PASSWORD/span>span stylecolor:#F97583> /span>span stylecolor:#9ECBFF> TOPSY KRETT PASSWORD/span>/span>span classline>span stylecolor:#79B8FF>ENCRYPTED_DATA/span>span stylecolor:#F97583> /span>span stylecolor:#9ECBFF> ENCRYPTED SSO SESSION/span>/span>span classline>/span>span classline>span stylecolor:#F97583>def/span>span stylecolor:#B392F0> derive_key_and_iv/span>span stylecolor:#E1E4E8>() -> Tuple/span>span stylecolor:#79B8FF>bytes/span>span stylecolor:#E1E4E8>, /span>span stylecolor:#79B8FF>bytes/span>span stylecolor:#E1E4E8>:/span>/span>span classline>span stylecolor:#E1E4E8> result /span>span stylecolor:#F97583>/span>span stylecolor:#79B8FF> bytes/span>span stylecolor:#E1E4E8>()/span>/span>span classline>/span>span classline>span stylecolor:#F97583> while/span>span stylecolor:#79B8FF> len/span>span stylecolor:#E1E4E8>(result) /span>span stylecolor:#F97583></span>span stylecolor:#79B8FF> KEY_SIZE/span>span stylecolor:#F97583> +/span>span stylecolor:#79B8FF> IV_SIZE/span>span stylecolor:#E1E4E8>:/span>/span>span classline>span stylecolor:#E1E4E8> hasher /span>span stylecolor:#F97583>/span>span stylecolor:#79B8FF> MD5/span>span stylecolor:#E1E4E8>.new()/span>/span>span classline>span stylecolor:#E1E4E8> hasher.update(result/span>span stylecolor:#F97583>-/span>span stylecolor:#79B8FF>IV_SIZE/span>span stylecolor:#E1E4E8>: /span>span stylecolor:#F97583>+/span>span stylecolor:#79B8FF> PASSWORD/span>span stylecolor:#E1E4E8>.encode(/span>span stylecolor:#9ECBFF>utf-8/span>span stylecolor:#E1E4E8>) /span>span stylecolor:#F97583>+/span>span stylecolor:#E1E4E8> salt)/span>/span>span classline>span stylecolor:#E1E4E8> result /span>span stylecolor:#F97583>+/span>span stylecolor:#E1E4E8> hasher.digest()/span>/span>span classline>/span>span classline>span stylecolor:#F97583> return/span>span stylecolor:#E1E4E8> result:/span>span stylecolor:#79B8FF>KEY_SIZE/span>span stylecolor:#E1E4E8>, result/span>span stylecolor:#79B8FF>KEY_SIZE/span>span stylecolor:#E1E4E8> : /span>span stylecolor:#79B8FF>KEY_SIZE/span>span stylecolor:#F97583> +/span>span stylecolor:#79B8FF> IV_SIZE/span>span stylecolor:#E1E4E8>/span>/span>span classline>/span>span classline>span stylecolor:#6A737D># get the raw encrypted data from the base64 encoded string/span>/span>span classline>span stylecolor:#E1E4E8>raw_encrypted_data /span>span stylecolor:#F97583>/span>span stylecolor:#E1E4E8> b64decode(/span>span stylecolor:#79B8FF>ENCRYPTED_DATA/span>span stylecolor:#E1E4E8>)/span>/span>span classline>/span>span classline>span stylecolor:#6A737D># the first block is Salted__THESALT, so we extract the salt/span>/span>span classline>span stylecolor:#E1E4E8>salt /span>span stylecolor:#F97583>/span>span stylecolor:#E1E4E8> raw_encrypted_data/span>span stylecolor:#79B8FF>SALT_SIZE/span>span stylecolor:#E1E4E8>:/span>span stylecolor:#79B8FF>BLOCK_SIZE/span>span stylecolor:#E1E4E8>/span>/span>span classline>/span>span classline>span stylecolor:#6A737D># beginning at the second block is the cipher text/span>/span>span classline>span stylecolor:#E1E4E8>cipher_text /span>span stylecolor:#F97583>/span>span stylecolor:#E1E4E8> raw_encrypted_data/span>span stylecolor:#79B8FF>BLOCK_SIZE/span>span stylecolor:#E1E4E8>:/span>/span>span classline>/span>span classline>span stylecolor:#6A737D># lets do some work/span>/span>span classline>span stylecolor:#E1E4E8>key, iv /span>span stylecolor:#F97583>/span>span stylecolor:#E1E4E8> derive_key_and_iv()/span>/span>span classline>span stylecolor:#E1E4E8>cipher /span>span stylecolor:#F97583>/span>span stylecolor:#79B8FF> AES/span>span stylecolor:#E1E4E8>.new(key, /span>span stylecolor:#79B8FF>AES/span>span stylecolor:#E1E4E8>./span>span stylecolor:#79B8FF>MODE_CBC/span>span stylecolor:#E1E4E8>, iv)/span>/span>span classline>span stylecolor:#E1E4E8>decrypted /span>span stylecolor:#F97583>/span>span stylecolor:#E1E4E8> cipher.decrypt(cipher_text)/span>/span>span classline>span stylecolor:#E1E4E8>unpadded /span>span stylecolor:#F97583>/span>span stylecolor:#E1E4E8> unpad(decrypted, /span>span stylecolor:#79B8FF>BLOCK_SIZE/span>span stylecolor:#E1E4E8>)/span>/span>span classline>/span>span classline>span stylecolor:#79B8FF>print/span>span stylecolor:#E1E4E8>(unpadded.decode(/span>span stylecolor:#9ECBFF>utf-8/span>span stylecolor:#E1E4E8>))/span>/span>/code>/pre>style> main li { padding-left: .5rem; } main li:not(:first-child) { margin-top: .5rem; } main ul { list-style-image: radial-gradient( ellipse at center, var(--accent-dark), var(--accent-dark) ); margin-top: 1rem; padding-left: 1rem; }/style> /section>section classmt-12 px-8 lg:relative lg:ml-auto lg:w-1/2> div> span classtext-gradient-tertiary whitespace-nowrap lg:text-gradient-secondary lg:absolute lg:right-full lg:top-1em lg:text-xs> Oct 4, 2023span classhidden pl-2 lg:inline>—/span> /span> h1 classfont-header font-bold py-1 text-accent-dark text-xl lg:text-2xl> a href/posts/late-to-the-tailwindcss-game>Late to the Tailwind game/a> /h1> /div> p>I know… I know! I’m late to thea hrefhttps://tailwindcss.com>Tailwind CSS/a> game. Today’s the day I put it into action onthis site, and I’m loving it so far. The support for arbitrary styles where a presetisn’t provided is critical. My hat’s off to you, Tailwind. I’ll continue to useBootstrap 5 under certain circumstances, but for new projects, hell to the naw./p>p>Did I mention that I’m simultaneously redoing the static generation ina hrefhttps://astro.build>Astro/a> too?/p> /section>section classmt-12 px-8 lg:relative lg:ml-auto lg:w-1/2> div> span classtext-gradient-tertiary whitespace-nowrap lg:text-gradient-secondary lg:absolute lg:right-full lg:top-1em lg:text-xs> Sep 7, 2023span classhidden pl-2 lg:inline>—/span> /span> h1 classfont-header font-bold py-1 text-accent-dark text-xl lg:text-2xl> a href/posts/released-celery-sqlalchemy>Released celery-sqlalchemy/a> /h1> /div> p>When it comes to distributed events in Python, a hrefhttps://docs.celeryq.dev>Celery/a>shines. It works flawlessly witha hrefhttps://www.rabbitmq.com>RabbitMQ/a> allowing you to run a limitless amount ofsimultaneous tasks across any number of servers./p>p>There is one drawback when using it witha hrefhttps://www.sqlalchemy.org>SQLAlchemy/a> models though, and that is that Celery doesn’tserialize them out-of-the-box. And even if it did, the data will not be deserializedback into a model on the other end./p>p>I created a hrefhttps://pypi.org/project/celery-sqlalchemy>celery-sqlalchemy/a> to solve thisvery problem so you can focus on writing code and stop worrying whether or not your datawill be correct on the other end./p>p>If you’re using Celery with SQLAlchemy and need to dispatch model tasksthen please give a hrefhttps://pypi.org/project/celery-sqlalchemy>celery-sqlalchemy/a> a try.I would appreciate any feedback./p> /section>section classmt-12 px-8 lg:relative lg:ml-auto lg:w-1/2> div> span classtext-gradient-tertiary whitespace-nowrap lg:text-gradient-secondary lg:absolute lg:right-full lg:top-1em lg:text-xs> May 31, 2023span classhidden pl-2 lg:inline>—/span> /span> h1 classfont-header font-bold py-1 text-accent-dark text-xl lg:text-2xl> a href/posts/released-postgresql-lock>Released postgresql-lock/a> /h1> /div> p>I developed this software specifically for CartHero’s requirements, butwanted to release it to the public since we found it a critical whenworking at high scale./p>p>The use-case was simple, we are firing hundreds and sometimes thousandsof events per second and need to prevent conflicts when writing newrecords and updating existing ones. There are redis libraries available,but I’m strictly talking about PostgreSQL records. And keeping mylocking mechanism as close to the database and with a guaranteed unlockon database connection exit was absolutely critical to our scenario./p>p>If you’re using Python & PostgreSQL and our use-case sounds similar,consider givinga hrefhttps://pypi.org/project/postgresql-lock>postgresql-lock/a> a try./p> /section>section classmt-12 px-8 lg:relative lg:ml-auto lg:w-1/2> div> span classtext-gradient-tertiary whitespace-nowrap lg:text-gradient-secondary lg:absolute lg:right-full lg:top-1em lg:text-xs> Mar 31, 2023span classhidden pl-2 lg:inline>—/span> /span> h1 classfont-header font-bold py-1 text-accent-dark text-xl lg:text-2xl> a href/posts/out-with-the-old-in-with-the-new>Out with the old, in with the new/a> /h1> /div> p>AppTrends had a great run, but like many great things, it was time tomove on. I started AppTrends in 2017 as a private Shopify app. At thetime all “private app” meant was that I couldn’t offer my app in theShopify app store./p>p>Shopify’s demands changed over time until eventually they deprecatedprivate apps in favor of their custom app offering. This gave me theopportunity to fix any shortcomings by developing a new platform withproper Shopify support. Today marks the completion of our user migrationto CartHero. There are a few remaining customers who ignored theflurry of notifications about migrating, but we’ll cross that bridgewhen we get there./p> /section>section classmt-12 px-8 lg:relative lg:ml-auto lg:w-1/2> div> span classtext-gradient-tertiary whitespace-nowrap lg:text-gradient-secondary lg:absolute lg:right-full lg:top-1em lg:text-xs> Apr 5, 2022span classhidden pl-2 lg:inline>—/span> /span> h1 classfont-header font-bold py-1 text-accent-dark text-xl lg:text-2xl> a href/posts/carthero-development-begins>CartHero development begins/a> /h1> /div> p>I partnered with Trent Fikes, a long-time AppTrends user and heavyhitter in the e-commerce apparel space, to bring our version of thesales funnel platform to the world. Today mark’s the day we breakground!/p>p>Our plan is to design a platform that natively supports customer ordersas they should be, which means from start to finish as a single order.This is exactly where other funnel sales platforms fall short, if nota slew of other reasons./p>p>On top of that, send your orders directly to ShipStation/ShipHero, orif needed Shopify. The point though is that tethering users to any singlefulfillment platform (I’m looking at you Shopify) is not the way./p> /section>section classmt-12 px-8 lg:relative lg:ml-auto lg:w-1/2> div> span classtext-gradient-tertiary whitespace-nowrap lg:text-gradient-secondary lg:absolute lg:right-full lg:top-1em lg:text-xs> Nov 2, 2017span classhidden pl-2 lg:inline>—/span> /span> h1 classfont-header font-bold py-1 text-accent-dark text-xl lg:text-2xl> a href/posts/apptrends-launch>AppTrends launch!/a> /h1> /div> p>I’m happy to announce after 5 months of development, my first SaaSproject. I teamed up with longtime friend Michael Velasquez to puttogether the best software available in seamless Shopify fulfillment./p>p>Taking orders outside of Shopify isn’t terribly difficult, but batchingmultiple customer orders and providing post-processing on those ordersis, which is where AppTrends steps in. We provide the perfect conduitbetween your customers and Shopify!/p> /section> /main> /body>/html>
View on OTX
|
View on ThreatMiner
Please enable JavaScript to view the
comments powered by Disqus.
Data with thanks to
AlienVault OTX
,
VirusTotal
,
Malwr
and
others
. [
Sitemap
]