-
Notifications
You must be signed in to change notification settings - Fork 8
Permalink
Choose a base ref
{{ refName }}
default
Choose a head ref
{{ refName }}
default
Comparing changes
Choose two branches to see what’s changed or to start a new pull request.
If you need to, you can also or
learn more about diff comparisons.
Open a pull request
Create a new pull request by comparing changes across two branches. If you need to, you can also .
Learn more about diff comparisons here.
base repository: SocketDev/socket-basics
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v3.3.0
Could not load branches
Nothing to show
Loading
Could not load tags
Nothing to show
{{ refName }}
default
Loading
...
head repository: SocketDev/socket-basics
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: v3.4.0
Could not load branches
Nothing to show
Loading
Could not load tags
Nothing to show
{{ refName }}
default
Loading
- 3 commits
- 22 files changed
- 2 contributors
Commits on Sep 18, 2026
-
chore(deps): bump socketdev in the python-minor-patch group (#117)
Bumps the python-minor-patch group with 1 update: [socketdev](https://github.com/SocketDev/socket-sdk-python). Updates `socketdev` from 3.5.0 to 3.6.0 - [Release notes](https://github.com/SocketDev/socket-sdk-python/releases) - [Changelog](https://github.com/SocketDev/socket-sdk-python/blob/main/CHANGELOG.md) - [Commits](SocketDev/socket-sdk-python@v3.5.0...v3.6.0) --- updated-dependencies: - dependency-name: socketdev dependency-version: 3.6.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: python-minor-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Configuration menu - View commit details
-
Copy full SHA for c866055 - Browse repository at this point
Copy the full SHA c866055View commit details -
fix(sast): mask credential values in finding snippets (#119)
* fix(sast): mask credentials in finding snippets The SAST connector copied OpenGrep's matched source line into `props.codeSnippet` and into the `detailedReport` markdown verbatim. For the hardcoded-credential rules the matched line is the credential, so the value reached `.socket.facts.json` in the scanned workspace, the Socket upload, the dashboard, and every notifier payload. Masking now happens once, where the alert is built, so the snippet, the detailed report, the dataflow trace and all nine notifiers inherit it: - Rules whose match is a credential get their string literals masked, keeping the assignment target, the syntax, the file and the line. Selection is by rule name, with a `redact` metadata key so custom rules can opt in or out. This covers 20 rules across all fifteen bundled language rule sets, not only the Python and JavaScript ones. - Every snippet, trace step and report, whatever rule produced it, is scrubbed of values matching a well-known credential format, since a rule unrelated to secrets can still match a line carrying one. Rules whose match is not a credential are untouched: `*-hardcoded-ip` and the password-policy rules keep their snippets, because masking those would remove the reason the finding was raised. Two fixes on the TruffleHog path, which was already masking: - `redactedValue` kept the first and last four characters of any value longer than eight, leaving most of a short password readable. Values under sixteen characters are now masked in full. - TruffleHog scanned the facts file this run writes, which lands inside the scan target, so a value another scanner recorded there was re-detected as a finding pointing at the output file rather than the source line. * test: assemble sample credentials so fixtures do not trip secret scanning The redaction tests need examples in published credential formats, which are the same formats a scanner walking this repository looks for. A literal is reported as a finding in its own right and blocks the push outright, so the Postgres connection string and the PEM block join the other samples in being built from a prefix and a body at runtime. .github/secret_scanning.yml excludes the file from alerts as a backstop. It is scoped to that one path, and it does not affect push protection -- assembling the value is what handles that. * refactor(config): build the API key source log from variable names The debug line reports which API key environment variables are set. It was built from a dict mapping each name to bool(os.environ.get(name)), then unpacked with .items() keeping only the key, so the value never reached the log -- but static analysis reads the dict as carrying the value into the log call and reports clear-text logging of a credential. Iterating a tuple of names and testing each for emptiness produces the same line from the same inputs, including the exclusion of an exported-but-empty variable, and leaves nothing for that reading to follow. * docs(changelog): align the release notes with the PR description * fix(redaction): close credential masking gaps * fix(redaction): keep multi-character operators and mask trace literals Two gaps Bugbot caught on the previous commit. The unquoted-assignment fallback matched a bare [=:], which stops on the first character of := or ==. The rest of the operator then heads the value, which no longer looks quoted, so the line took the unquoted branch and was starred out whole -- losing the operator and quotes the literal pass exists to keep. go-hardcoded-credentials matches $VAR := "...", so this reached real findings. = and : now match only where they are not part of a longer operator, and a comparison assigns nothing so it no longer matches at all. redact_dataflow_trace ran only the token scrub, so a trace step kept a generic password that the vendor-format patterns do not recognize. It now takes the credential flag and gives each step the same treatment as the snippet. Only a taint rule declaring redact in its metadata reaches this today, since no bundled credential rule is taint-mode, but the trace should not be the one field that keeps the value. * fix(redaction): anchor short metavariable replacements in rule messages A bound metavariable is masked by replacing its value in the expanded message. A short value is also an ordinary substring, so the replace rewrote words that merely contained it: binding "a" turned "secret a is bad" into "secret * is b*d". Values below eight characters are now replaced only between non-word boundaries. Skipping them is not an option -- a short credential still has to be masked -- and longer values stay an unanchored replace, being specific enough not to collide.
Configuration menu - View commit details
-
Copy full SHA for fe0b636 - Browse repository at this point
Copy the full SHA fe0b636View commit details
Commits on Sep 19, 2026
-
* chore(release): 3.4.0 Stamps [Unreleased] as [3.4.0], bumps the version files and uv.lock, and synchronizes the current-release references across README.md and docs/**. Minor rather than patch: #119 changes the snippet, detailed report, dataflow trace and description that every consumer of a finding reads, and adds the `redact` rule-metadata key. Also records the socketdev 3.5.0 -> 3.6.0 lockfile bump from #117, which merged without a changelog entry. * fix(redaction): treat plain-text-password as logic, not a credential The rule that fragment names matches password *handling* -- assigning request input to a password field, or comparing against one -- so its match is an expression rather than a literal. Running the literal pass on it reduced `user.password = request.form.get('password')` to a row of asterisks, which is the rule's main pattern and leaves nothing to act on. It belongs with `hardcoded-ip` and the password-policy rules, which the same comment already excludes for the same reason. A comparison against a hardcoded value is the one shape it covers that carries a credential, and that is what the `hardcoded-*` rules are for. Also folds a duplicated TestRedactMessage class into one. The second definition shadowed the first, so two message tests never ran. * fix(redaction): serve both shapes of the password-logic rule Dropping plain-text-password from the credential fragments fixed the over-masking but opened a hole: one of that rule's patterns is a comparison against a hardcoded string, and no hardcoded-* rule matches that shape, so `if user.password == "hunter2"` went into the facts file verbatim. Confirmed by scanning a file with exactly that line. The fragment goes back, and the over-masking is fixed where it belongs. An assigned value that calls something is an expression, not a bare credential, so it skips the unquoted fallback and the literal pass masks just the quoted parts. `user.password = request.form.get('password')` keeps its expression, the comparison value is masked, and a bare value with a trailing comment is still masked whole so a short credential cannot be partly revealed. * fix(redaction): bind the right operator and mask trailing comments Three defects in the unquoted-assignment fallback, found by working through the shapes the credential rules actually produce. A call matched anywhere in the value skipped masking entirely, so `password: hunter2 # see get_secret()` kept the credential. The check is now anchored: only a value that opens with a call is treated as an expression. The first operator on the line bound, so a type annotation won over the assignment after it and `password: str = "..."` was starred out whole rather than reaching the literal pass -- ordinary Python and TypeScript. The last operator now binds, and operators covered by a string literal are skipped so the `:` in `url = "https://..."` cannot bind either. That needs the literal spans, which one regex cannot express, so _split_assignment walks the matches. Masking the value left a comment beside it holding the plaintext, as in `password = get_secret() # real value is hunter2`. Text after an unquoted comment marker is now masked too. Also stops measuring an unquoted value together with whatever follows it. `password: hunter2 # plain comment` is long enough for a partial reveal even though `hunter2` is not, and it was rendering as `password: hunt...ment`. * fix(redaction): take the comment off first and mask per statement Two ways a credential stayed in the part of the line the value search never looked at. A comment can hold an operator later in the line than the real one. Because the comment was masked after the operator was chosen, that one bound and the value in front of it was left in the head: `password = hunter2 # see x = y` kept hunter2. The comment now comes off before anything else reads the line. A line can also carry more than one statement, and only one operator binds per statement, so `a = hunter2; password = x` masked the second value and left the first. Masking now runs per statement, split on separators outside string literals. * fix(redaction): measure literal spans over the snippet, not per line A literal can open on one line and close on another. Spans were computed per line, so a marker on a literal's second line read as a comment and the rest of that line was starred -- dropping the closing quote, after which the literal pass no longer matched and the opening line's value survived. Spans are now measured once over the whole snippet and consulted by absolute position. Two things fall out of that. A snippet is a slice of a file, so a literal can also never close. The quoted value branch deferred to the literal pass, which never matches an unterminated literal, so `password = "hunter2` was left untouched. It now defers only when the quote opens a span the pass can find, and masks the value whole otherwise. A multi-line literal body was measured as one value, so the head-and-tail reveal exposed the start of its first line. Each line of such a body is now masked whole, with the line breaks kept so the snippet still shows where the literal begins and ends. * fix(redaction): recognize string prefixes and interpolated bodies A prefixed opener such as r""" or f""" was not read as opening a literal, so the opening line was starred and its quotes were removed. Later lines were still measured against the original spans, which said they were inside a literal, so nothing masked them and the final literal pass no longer matched. The prefix is now part of the opener check. That alone left a partial reveal: literal text around an interpolation inflates the body past the reveal threshold, so f"{b}_SuperSecret123!" showed 123!. An interpolated body is masked whole, on the same reasoning as a multi-line one -- it is a block of content, not a single opaque value. * fix(redaction): do not defer on a spurious empty-literal match An unterminated triple-quoted value still produces a literal match: the engine backtracks past the triple alternative and reads the first two quotes as an empty string. That match was enough to send the value to the masking pass, which then covered only those two quotes, so the credential stayed in the snippet. Affects bare and prefixed openers alike. Deferring now requires a span that starts at the quote and holds both delimiters, which an empty match cannot satisfy. _STRING_LITERAL also gained triple-quoted alternatives so a terminated block matches once with its real body rather than as an empty string followed by a second literal. * fix(redaction): fail safe when string state is lost, and fuzz the invariant A generated-snippet sweep found three gaps the hand-written cases did not, all of them the same thing: masking depends on knowing where string literals start and end, and a truncated snippet can make that unknowable. An unterminated literal is not reached by the assignment fallback when the value sits in a comparison or a call argument, so it went to the masking pass, which cannot match it. A quote outside every matched span now marks the rest of the line as literal content. An unclosed triple-quoted block does not simply fail to match -- its first two quotes match as an empty string and the third pairs with any stray quote later, producing one long span that hides a real assignment on a later line. An odd count of triple delimiters now masks from the opener to the end. A " or ' literal cannot hold a raw newline in any language these rules cover, so a match that does is the same pairing artifact rather than a literal. Those spans are discarded; backticks and triple quotes keep theirs. tests/test_secret_redaction_fuzz.py generates the combinations rather than listing them, and asserts no credential survives, none is partly revealed, and non-credential snippets come through unchanged. 2,000,000 generated cases pass; 20,000 run in CI in about a second. * fix(redaction): mask to the end of a snippet once string state is lost Bugbot found that a credential on a continuation line survived, and extending the fuzzer to put the secret after the line break -- it had only ever put it before -- found a second case immediately. Both are the same thing: masking that stops at the opening line. Where a literal opens and its end is unknowable, everything after is inside it as far as any reader can tell, so masking now runs to the end of the snippet rather than the end of the line. The two ways state is lost -- a quote no surviving span covers, and an odd number of triple delimiters -- are handled together instead of separately. The second case was the opposite failure. Masking an unquoted value whole destroyed the opening quote of a literal that continued past the line, so the snippet-wide pass afterwards no longer matched and the rest of the literal was left alone. Such a value is now masked only up to the opener, and the pass takes the literal itself. Both generators put the secret on either side of a line break, so the shape is covered from here on. 1,000,000 generated cases pass. * fix(redaction): fail closed on ambiguous credential syntax
Configuration menu - View commit details
-
Copy full SHA for d8ef2e5 - Browse repository at this point
Copy the full SHA d8ef2e5View commit details
Loading
This comparison is taking too long to generate.
Unfortunately it looks like we can’t render this comparison for you right now. It might be too big, or there might be something weird with your repository.
You can try running this command locally to see the comparison on your machine:
git diff v3.3.0...v3.4.0