Skip to content

fix(content-type): replace git-diff with jsdiff to clear Snyk issue - #324

Merged
netrajpatel merged 5 commits into
developmentfrom
fix/snyk-replace-git-diff-with-jsdiff
Aug 5, 2026
Merged

fix(content-type): replace git-diff with jsdiff to clear Snyk issue#324
netrajpatel merged 5 commits into
developmentfrom
fix/snyk-replace-git-diff-with-jsdiff

Conversation

@naman-contentstack

Copy link
Copy Markdown
Contributor

Why

git-diff@2.0.7 is unmaintained (last published 2018) and pulls in chalk@2, diff@3, loglevel, shelljs and shelljs.exec. Snyk flagged the only vulnerable path in the entire monorepo through that tree:

✗ Missing Release of Resource after Effective Lifetime [Medium]
  SNYK-JS-INFLIGHT-6095116
  git-diff@2.0.7 > shelljs@0.8.5 > glob@7.2.3 > inflight@1.0.6

Snyk lists it under "Issues with no direct upgrade or patch" — because git-diff is abandoned, there is nothing to bump to. The dependency has to go.

What

Replaced it with diff@^9 (jsdiff): zero runtime dependencies, bundled TypeScript types, dual CJS/ESM. As a side effect it also drops the dependency on a git binary and the temp-file + subprocess round trip git-diff used to shell out through shelljs.

import {createTwoFilesPatch} from 'diff'

function buildDiffString(previous: any, current: any) {
  return createTwoFilesPatch(
    previous.uid, current.uid,
    JSON.stringify(previous, null, 2), JSON.stringify(current, null, 2),
    current.updated_at, current.updated_at,
  )
}

createTwoFilesPatch emits the --- / +++ file headers itself, so the hand-rolled header concatenation is gone. @types/git-diff dropped too, since diff ships its own types.

Output equivalence

Not assumed — checked. Both the old and new patch strings were run through Diff2html.parse and compared on file names, added/deleted line counts, hunk headers, and every line's type and content. Identical. The rendered compare view is unchanged.

One behaviour actually improves: git-diff returned undefined when both sides matched, which interpolated the literal string "undefined" into the patch. createTwoFilesPatch returns a well-formed patch with no hunks.

Tests

The existing spec mocked diff2html, so the generated patch was never asserted — the diff-building logic had no real coverage. Added two cases against the string handed to Diff2html.parse:

  • headers, @@ hunk, and the actual changed -/+ lines
  • identical-input case: no hunks, and no literal "undefined"

Both were mutation-checked — corrupting buildDiffString makes them fail, so they catch regressions rather than just passing.

tsc -b --force   exit 0
Test Suites:     16 passed, 16 total
Tests:           78 passed, 78 total   (was 76)
snyk test --all-projects   no vulnerable paths, monorepo-wide

Reviewer notes

  • .talismanrc: the pinned pnpm-lock.yaml checksum is bumped. Talisman flags the lockfile's sha512-... npm integrity hashes as "base64 encoded text" — a false positive, not a secret. The file is already ignored by checksum; the lockfile change invalidated the pin. Same thing 38bebe6 and db3fcc5 did. The pre-commit hook was not bypassed — Talisman and Snyk both ran and passed.
  • Version bump: this branch also carries a pre-existing, uncommitted 1.5.31.5.4 bump that was already in the working tree, in the same package.json as the dep change and not cleanly separable. Happy to pull it out if you'd rather it landed on its own.
  • Pre-existing bug, deliberately not fixed heresrc/core/content-type/compare.ts:18:
    tmp.file({...}, async function (err: any, ...) {
      if (err) throw err
    The callback is async and nothing awaits it, so a real tmp failure becomes an unhandled rejection and kills the CLI instead of surfacing a readable error. This is the one uncovered branch in the coverage report. Out of scope for a dependency swap — worth a follow-up ticket.

🤖 Generated with Claude Code

git-diff@2.0.7 is unmaintained (last published 2018) and pulls in
chalk@2, diff@3, loglevel, shelljs and shelljs.exec. Snyk flagged the
only vulnerable path in the monorepo through that tree:

  Missing Release of Resource after Effective Lifetime [Medium]
  SNYK-JS-INFLIGHT-6095116
  git-diff@2.0.7 > shelljs@0.8.5 > glob@7.2.3 > inflight@1.0.6

Snyk reports no direct upgrade or patch, since git-diff is abandoned.

Replace it with diff@^9 (jsdiff): zero runtime dependencies, bundled
TypeScript types, dual CJS/ESM. It also removes the dependency on a
`git` binary and the temp-file/subprocess round trip git-diff used to
shell out through shelljs.

buildDiffString now calls createTwoFilesPatch, which emits the `---`
and `+++` file headers itself, so the hand-rolled header concatenation
is gone. Output was verified equivalent: both patch strings were run
through Diff2html.parse and compared on file names, added/deleted line
counts, hunk headers and every line type plus content - identical.

Identical inputs are also handled better. git-diff returned undefined
when both sides matched, which interpolated the literal string
"undefined" into the patch; createTwoFilesPatch returns a well-formed
patch with no hunks.

Drop @types/git-diff, since diff ships its own types.

Tests: the existing spec mocked diff2html, so the generated patch was
never asserted. Add two cases covering the string handed to
Diff2html.parse - one for headers, hunk and changed lines, one for the
identical-input case. Both were mutation-checked against a corrupted
buildDiffString.

Suite: 78 passed, 16 suites, tsc clean, Snyk reports no vulnerable
paths across the monorepo.

Bump the pinned pnpm-lock.yaml checksum in .talismanrc, which the
lockfile change invalidates. The finding is the usual sha512 integrity
hashes, not a secret.

Note: this commit also carries a pre-existing, uncommitted version bump
to 1.5.4 that was already present in package.json.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@naman-contentstack
naman-contentstack requested a review from a team as a code owner July 30, 2026 09:12
@github-actions

Copy link
Copy Markdown

🔒 Security Scan Results

ℹ️ Note: Only vulnerabilities with available fixes (upgrades or patches) are counted toward thresholds.

Check Type Count (with fixes) Without fixes Threshold Result
🔴 Critical Severity 0 0 10 ✅ Passed
🟠 High Severity 0 0 25 ✅ Passed
🟡 Medium Severity 0 0 500 ✅ Passed
🔵 Low Severity 0 0 1000 ✅ Passed

⏱️ SLA Breach Summary

✅ No SLA breaches detected. All vulnerabilities are within acceptable time thresholds.

Severity Breaches (with fixes) Breaches (no fixes) SLA Threshold (with/no fixes) Status
🔴 Critical 0 0 15 / 30 days ✅ Passed
🟠 High 0 0 30 / 120 days ✅ Passed
🟡 Medium 0 0 90 / 365 days ✅ Passed
🔵 Low 0 0 180 / 365 days ✅ Passed

✅ BUILD PASSED - All security checks passed

netrajpatel
netrajpatel previously approved these changes Aug 4, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR updates the contentstack-cli-content-type plugin to eliminate the unmaintained git-diff dependency (and its vulnerable transitive tree) by switching unified-patch generation to diff (jsdiff), while keeping the existing compare HTML output flow via diff2html.

Changes:

  • Replaced git-diff with diff’s createTwoFilesPatch in the content-type compare output generator.
  • Strengthened Jest coverage by asserting the unified patch string passed into Diff2html.parse (including the identical-input case).
  • Updated dependency/lockfile state, docs references to the new diff implementation, and the .talismanrc lockfile checksum; bumped the package version.

Reviewed changes

Copilot reviewed 6 out of 7 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
pnpm-lock.yaml Removes git-diff and related transitive deps, adds diff, and updates lockfile resolutions.
packages/contentstack-content-type/src/core/content-type/compare.ts Switches diff generation from git-diff to diff.createTwoFilesPatch.
packages/contentstack-content-type/tests/core/content-type/compare.test.ts Adds assertions on the generated patch string provided to diff2html.
packages/contentstack-content-type/skills/contentstack-cli-content-type/SKILL.md Updates documentation to reflect diff usage instead of git-diff.
packages/contentstack-content-type/skills/code-review/SKILL.md Updates dependency review checklist to replace git-diff with diff.
packages/contentstack-content-type/package.json Drops git-diff / @types/git-diff, adds diff, and bumps version to 1.5.4.
.talismanrc Updates the pinned checksum for pnpm-lock.yaml.
Files not reviewed (1)
  • pnpm-lock.yaml: Generated file

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread pnpm-lock.yaml Outdated
Comment thread pnpm-lock.yaml Outdated
Comment thread packages/contentstack-content-type/package.json
shafeeqd959
shafeeqd959 previously approved these changes Aug 4, 2026
@netrajpatel
netrajpatel dismissed stale reviews from shafeeqd959 and themself via e2d2fca August 5, 2026 06:12
@snyk-io

snyk-io Bot commented Aug 5, 2026

Copy link
Copy Markdown

Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues
Licenses 0 0 0 0 0 issues
Code Security 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown

🔒 Security Scan Results

ℹ️ Note: Only vulnerabilities with available fixes (upgrades or patches) are counted toward thresholds.

Check Type Count (with fixes) Without fixes Threshold Result
🔴 Critical Severity 0 0 10 ✅ Passed
🟠 High Severity 0 0 25 ✅ Passed
🟡 Medium Severity 0 0 500 ✅ Passed
🔵 Low Severity 0 0 1000 ✅ Passed

⏱️ SLA Breach Summary

✅ No SLA breaches detected. All vulnerabilities are within acceptable time thresholds.

Severity Breaches (with fixes) Breaches (no fixes) SLA Threshold (with/no fixes) Status
🔴 Critical 0 0 15 / 30 days ✅ Passed
🟠 High 0 0 30 / 120 days ✅ Passed
🟡 Medium 0 0 90 / 365 days ✅ Passed
🔵 Low 0 0 180 / 365 days ✅ Passed

✅ BUILD PASSED - All security checks passed

netrajpatel
netrajpatel previously approved these changes Aug 5, 2026
The merge of development into this branch left pnpm-lock.yaml internally
inconsistent: an eslint-import-resolver-typescript@3.10.1 snapshot still
referenced get-tsconfig@4.14.0 while the packages section had advanced to
4.14.1, and several peer-dependency snapshots (ts-node, @tsconfig/*, jest,
eslint resolver permutations) had been dropped.

This made `pnpm install --frozen-lockfile` fail with
ERR_PNPM_LOCKFILE_MISSING_DEPENDENCY, breaking the run-tests and
tsgen-integration jobs at the install step before any test could run.

Regenerated with `pnpm install --no-frozen-lockfile` using pnpm 10.28.0,
matching the pinned packageManager. Lockfile-only change: no package.json
or dependency versions altered. Refreshed the pnpm-lock.yaml checksum in
.talismanrc to match, per existing repo practice.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown

🔒 Security Scan Results

ℹ️ Note: Only vulnerabilities with available fixes (upgrades or patches) are counted toward thresholds.

Check Type Count (with fixes) Without fixes Threshold Result
🔴 Critical Severity 0 0 10 ✅ Passed
🟠 High Severity 0 0 25 ✅ Passed
🟡 Medium Severity 0 0 500 ✅ Passed
🔵 Low Severity 0 0 1000 ✅ Passed

⏱️ SLA Breach Summary

✅ No SLA breaches detected. All vulnerabilities are within acceptable time thresholds.

Severity Breaches (with fixes) Breaches (no fixes) SLA Threshold (with/no fixes) Status
🔴 Critical 0 0 15 / 30 days ✅ Passed
🟠 High 0 0 30 / 120 days ✅ Passed
🟡 Medium 0 0 90 / 365 days ✅ Passed
🔵 Low 0 0 180 / 365 days ✅ Passed

✅ BUILD PASSED - All security checks passed

netrajpatel
netrajpatel previously approved these changes Aug 5, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 6 out of 7 changed files in this pull request and generated no new comments.

Files not reviewed (1)
  • pnpm-lock.yaml: Generated file
Suppressed comments (1)

packages/contentstack-content-type/skills/contentstack-cli-content-type/SKILL.md:103

  • The docs refer to the diff builder as diff, which is ambiguous (could be read as the unix diff tool). Clarifying that this is the npm diff (jsdiff) package will make the dependency swap easier to understand for future maintainers.
- **Compare**: `core/content-type/compare.ts` builds a unified diff from two JSON snapshots (`diff`), parses with **diff2html**, writes a **temporary HTML** file, opens it in the browser (`cli-ux` / `cli.open`). Not a terminal table.

…gan types

Addresses Copilot review feedback on #324. contentstack-content-type was
the only package of 20 declaring @types/* under dependencies; the other 19
keep them in devDependencies. Type packages are compile-time only and
nothing here re-exports their types publicly, so shipping them as runtime
deps only inflated the production install.

Moved @types/diff2html, @types/table and @types/tmp to devDependencies.
The runtime packages they describe (diff2html, table, tmp) stay in
dependencies.

Dropped @types/hogan.js entirely rather than moving it: hogan is
referenced nowhere in this package's src or tests, and hogan.js is not a
runtime dependency of any package in the repo, so the types were dead
weight.

Verified with `tsc -b --force` (clean, emits lib/) and the package suite
(16 suites, 78 tests passing). Talisman checksum for pnpm-lock.yaml
refreshed via `talisman --checksum`.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown

🔒 Security Scan Results

ℹ️ Note: Only vulnerabilities with available fixes (upgrades or patches) are counted toward thresholds.

Check Type Count (with fixes) Without fixes Threshold Result
🔴 Critical Severity 0 0 10 ✅ Passed
🟠 High Severity 0 0 25 ✅ Passed
🟡 Medium Severity 0 0 500 ✅ Passed
🔵 Low Severity 0 0 1000 ✅ Passed

⏱️ SLA Breach Summary

✅ No SLA breaches detected. All vulnerabilities are within acceptable time thresholds.

Severity Breaches (with fixes) Breaches (no fixes) SLA Threshold (with/no fixes) Status
🔴 Critical 0 0 15 / 30 days ✅ Passed
🟠 High 0 0 30 / 120 days ✅ Passed
🟡 Medium 0 0 90 / 365 days ✅ Passed
🔵 Low 0 0 180 / 365 days ✅ Passed

✅ BUILD PASSED - All security checks passed

@netrajpatel
netrajpatel requested a review from shafeeqd959 August 5, 2026 07:08
@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown

🔒 Security Scan Results

ℹ️ Note: Only vulnerabilities with available fixes (upgrades or patches) are counted toward thresholds.

Check Type Count (with fixes) Without fixes Threshold Result
🔴 Critical Severity 0 0 10 ✅ Passed
🟠 High Severity 0 0 25 ✅ Passed
🟡 Medium Severity 0 0 500 ✅ Passed
🔵 Low Severity 0 0 1000 ✅ Passed

⏱️ SLA Breach Summary

✅ No SLA breaches detected. All vulnerabilities are within acceptable time thresholds.

Severity Breaches (with fixes) Breaches (no fixes) SLA Threshold (with/no fixes) Status
🔴 Critical 0 0 15 / 30 days ✅ Passed
🟠 High 0 0 30 / 120 days ✅ Passed
🟡 Medium 0 0 90 / 365 days ✅ Passed
🔵 Low 0 0 180 / 365 days ✅ Passed

✅ BUILD PASSED - All security checks passed

@netrajpatel
netrajpatel merged commit 05d2c52 into development Aug 5, 2026
10 checks passed
@netrajpatel
netrajpatel deleted the fix/snyk-replace-git-diff-with-jsdiff branch August 5, 2026 07:11
netrajpatel added a commit that referenced this pull request Aug 5, 2026
Brings in the 27 commits development gained after the previous merge
(PR #217 Asset-Scanning-v1, #322, #323 and others), clearing the conflict
that was blocking PR #243 and preventing GitHub Actions from producing a
merge ref.

Only .talismanrc conflicted. Resolved to the checksum for the merged
pnpm-lock.yaml (95ad8483..., confirmed via `talisman --checksum`) and moved
`version` back to the top level, where talisman expects it — development's
side had it indented under the fileignoreconfig entry.

Verified before push:
  - `pnpm install --frozen-lockfile` passes (the auto-merged lockfile is
    self-consistent; this is the check that caught the broken lockfile in
    PR #324 and the dropped `open` dependency in the previous merge)
  - dependency sweep against both merge parents: nothing lost
  - undeclared-import sweep unchanged from baseline: no new hybrids
  - test suites for all six packages this merge touched:
      contentstack-audit             160 passing
      contentstack-export            485 passing
      contentstack-external-migrate   42 passing
      contentstack-import           1736 passing
      contentstack-query-export      139 passing
      contentstack-bulk-publish      (no test script)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.

4 participants