Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

The test/resolver/malformed_package_json/package.json leads to an fatal error when starting meteor #271

Closed
gitJoe42 opened this issue Jan 13, 2022 · 39 comments

Comments

@gitJoe42
Copy link

Hello, I deleted the node_modules folder in my meteor app and made meteor npm install. After that the app can't be started and ends up with the following error:

=> Started proxy.
/Users/jochen/.meteor/packages/semantic_ui/.2.2.6_5.h77ycc.hbbb4++os+web.browser+web.cordova/plugin.generateSemanticUi.os/npm/node_modules/meteor/promise/node_modules/meteor-promise/promise_server.js:165
      throw error;
      ^

SyntaxError: Unexpected end of JSON input
    at JSON.parse (<anonymous>)
    at /Users/jochen/.meteor/packages/meteor-tool/.2.4.0_1.1npmc7f.35nt++os.osx.x86_64+web.browser+web.browser.legacy+web.cordova/mt-os.osx.x86_64/tools/fs/tools/fs/optimistic.ts:321:17
    at wrap.makeCacheKey (/Users/jochen/.meteor/packages/meteor-tool/.2.4.0_1.1npmc7f.35nt++os.osx.x86_64+web.browser+web.browser.legacy+web.cordova/mt-os.osx.x86_64/tools/fs/tools/fs/optimistic.ts:36:15)
    at recomputeNewValue (/Users/jochen/.meteor/packages/meteor-tool/.2.4.0_1.1npmc7f.35nt++os.osx.x86_64+web.browser+web.browser.legacy+web.cordova/mt-os.osx.x86_64/dev_bundle/lib/node_modules/optimism/src/entry.ts:198:31)
    at Slot.withValue (/Users/jochen/.meteor/packages/meteor-tool/.2.4.0_1.1npmc7f.35nt++os.osx.x86_64+web.browser+web.browser.legacy+web.cordova/mt-os.osx.x86_64/dev_bundle/lib/node_modules/@wry/context/lib/context.esm.js:69:29)
    at reallyRecompute (/Users/jochen/.meteor/packages/meteor-tool/.2.4.0_1.1npmc7f.35nt++os.osx.x86_64+web.browser+web.browser.legacy+web.cordova/mt-os.osx.x86_64/dev_bundle/lib/node_modules/optimism/src/entry.ts:181:19)
    at Entry.recompute (/Users/jochen/.meteor/packages/meteor-tool/.2.4.0_1.1npmc7f.35nt++os.osx.x86_64+web.browser+web.browser.legacy+web.cordova/mt-os.osx.x86_64/dev_bundle/lib/node_modules/optimism/src/entry.ts:91:9)
    at optimistic (/Users/jochen/.meteor/packages/meteor-tool/.2.4.0_1.1npmc7f.35nt++os.osx.x86_64+web.browser+web.browser.legacy+web.cordova/mt-os.osx.x86_64/dev_bundle/lib/node_modules/optimism/src/index.ts:150:25)
    at /Users/jochen/.meteor/packages/meteor-tool/.2.4.0_1.1npmc7f.35nt++os.osx.x86_64+web.browser+web.browser.legacy+web.cordova/mt-os.osx.x86_64/tools/fs/tools/fs/optimistic.ts:366:19
    at recomputeNewValue (/Users/jochen/.meteor/packages/meteor-tool/.2.4.0_1.1npmc7f.35nt++os.osx.x86_64+web.browser+web.browser.legacy+web.cordova/mt-os.osx.x86_64/dev_bundle/lib/node_modules/optimism/src/entry.ts:198:31)
    at Slot.withValue (/Users/jochen/.meteor/packages/meteor-tool/.2.4.0_1.1npmc7f.35nt++os.osx.x86_64+web.browser+web.browser.legacy+web.cordova/mt-os.osx.x86_64/dev_bundle/lib/node_modules/@wry/context/lib/context.esm.js:69:29)
    at reallyRecompute (/Users/jochen/.meteor/packages/meteor-tool/.2.4.0_1.1npmc7f.35nt++os.osx.x86_64+web.browser+web.browser.legacy+web.cordova/mt-os.osx.x86_64/dev_bundle/lib/node_modules/optimism/src/entry.ts:181:19)
    at Entry.recompute (/Users/jochen/.meteor/packages/meteor-tool/.2.4.0_1.1npmc7f.35nt++os.osx.x86_64+web.browser+web.browser.legacy+web.cordova/mt-os.osx.x86_64/dev_bundle/lib/node_modules/optimism/src/entry.ts:91:9)
    at optimistic (/Users/jochen/.meteor/packages/meteor-tool/.2.4.0_1.1npmc7f.35nt++os.osx.x86_64+web.browser+web.browser.legacy+web.cordova/mt-os.osx.x86_64/dev_bundle/lib/node_modules/optimism/src/index.ts:150:25)
    at find (/tools/isobuild/package-source.js:1344:30)
    at /tools/isobuild/package-source.js:1400:27
    at Array.forEach (<anonymous>)
    at find (/tools/isobuild/package-source.js:1379:22)
    at /tools/isobuild/package-source.js:1400:27
    at Array.forEach (<anonymous>)
    at find (/tools/isobuild/package-source.js:1379:22)
    at /tools/isobuild/package-source.js:1400:27
    at Array.forEach (<anonymous>)
    at find (/tools/isobuild/package-source.js:1379:22)
    at /tools/isobuild/package-source.js:1400:27
    at Array.forEach (<anonymous>)
    at find (/tools/isobuild/package-source.js:1379:22)
    at find (/tools/isobuild/package-source.js:1411:25)
    at /tools/isobuild/package-source.js:1423:34
    at Object.withCache (/Users/jochen/.meteor/packages/meteor-tool/.2.4.0_1.1npmc7f.35nt++os.osx.x86_64+web.browser+web.browser.legacy+web.cordova/mt-os.osx.x86_64/tools/fs/tools/fs/files.ts:1662:18)
    at PackageSource._findSources (/tools/isobuild/package-source.js:1423:18)
    at SourceArch.getFiles (/tools/isobuild/package-source.js:965:32)
    at /tools/isobuild/compiler.js:406:23
    at /tools/isobuild/compiler.js:186:28
    at Object.withCache (/Users/jochen/.meteor/packages/meteor-tool/.2.4.0_1.1npmc7f.35nt++os.osx.x86_64+web.browser+web.browser.legacy+web.cordova/mt-os.osx.x86_64/tools/fs/tools/fs/files.ts:1662:18)
    at /tools/isobuild/compiler.js:185:11
    at Function.each (/Users/jochen/.meteor/packages/meteor-tool/.2.4.0_1.1npmc7f.35nt++os.osx.x86_64+web.browser+web.browser.legacy+web.cordova/mt-os.osx.x86_64/dev_bundle/lib/node_modules/underscore/underscore-node-f-pre.js:1316:7)
    at Object.compile (/tools/isobuild/compiler.js:180:5)
    at /tools/isobuild/bundler.js:3291:24
    at Object.capture (/tools/utils/buildmessage.js:283:5)
    at bundle (/tools/isobuild/bundler.js:3237:31)
    at /tools/isobuild/bundler.js:3180:32
    at Slot.withValue (/Users/jochen/.meteor/packages/meteor-tool/.2.4.0_1.1npmc7f.35nt++os.osx.x86_64+web.browser+web.browser.legacy+web.cordova/mt-os.osx.x86_64/dev_bundle/lib/node_modules/@wry/context/lib/context.esm.js:69:29)
    at Object.withCache (/Users/jochen/.meteor/packages/meteor-tool/.2.4.0_1.1npmc7f.35nt++os.osx.x86_64+web.browser+web.browser.legacy+web.cordova/mt-os.osx.x86_64/tools/fs/tools/fs/files.ts:1662:39)
    at Object.bundle (/tools/isobuild/bundler.js:3180:16)
    at /tools/runners/run-app.js:581:24
    at Function.run (/Users/jochen/.meteor/packages/meteor-tool/.2.4.0_1.1npmc7f.35nt++os.osx.x86_64+web.browser+web.browser.legacy+web.cordova/mt-os.osx.x86_64/tools/tool-env/tools/tool-env/profile.ts:289:14)
    at bundleApp (/tools/runners/run-app.js:580:34)
    at AppRunner._runOnce (/tools/runners/run-app.js:627:35)
    at AppRunner._fiber (/tools/runners/run-app.js:949:28)
    at /tools/runners/run-app.js:410:12

When I changed resolve/test/resolver/malformed_package_json/package.json to a well formed package.json by adding a '}' everything is fine.

@ljharb
Copy link
Member

ljharb commented Jan 13, 2022

This seems like a bug in meteor; there’s almost no reason to ever be reading a non-package-level package.json file in node_modules, and nothing in node_modules should be expected to be well-formed.

See #264.

@ljharb
Copy link
Member

ljharb commented Jan 13, 2022

Actually it looks like a bug in meteor-tool specifically, based on the stack trace. Please file an issue and link it here.

@gitJoe42
Copy link
Author

there’s almost no reason to ever be reading a non-package-level package.json file in node_modules

Good point.

@ljharb
Copy link
Member

ljharb commented Jan 13, 2022

(to be clear: the only reason is to look for "main" or "type": "module" when doing resolution, and in either case, the only correct thing to do is ignore unparseable files, just like node itself does)

@ngraef
Copy link

ngraef commented Jan 21, 2022

Can I ask why resolve publishes the test directory in its package in the first place? In my opinion, test/ should be in .npmignore.

I found this issue because I was using a different tool that scans all package.json files under node_modules, and it crashed on test/resolver/malformed_package_json/package.json. Yes, that tool needs to be fixed to ignore malformed input; however resolve could also be a good NPM citizen by cleaning up its releases.

@ljharb
Copy link
Member

ljharb commented Jan 21, 2022

@ngraef because npm explore foo && npm install && npm test should always work.

Good npm citizens publish their tests, in my opinion.

@ngraef
Copy link

ngraef commented Jan 22, 2022

because npm explore foo && npm install && npm test should always work.

I don't agree with that statement, because in many cases it would require publishing nearly the entire contents of the project in the package. I believe making it easy to develop on the module is the job of the source repo, not the release artifacts. Regardless, this is not my package to maintain. Thank you for explaining the reasoning behind the decision, and thank you for all the maintenance work you do.

@andyedwardsibm
Copy link

Another scenario is where Twistlock/Prisma scans of images that contain resolve report scan errors due to the malformed package.json files. In that case the scanner is hunting through the entire image looking for package.json files and scanning those "test" files.

Would a possible "solution" be to not have package.json files in the test folder but to mock out fs.readFile() in each test? That was you still get to have the tests included in the module but you don't then have deliberately-invalid package.json files on disk

@ljharb
Copy link
Member

ljharb commented Jan 25, 2022

@andyedwardsibm thats still a bug in that tool - both because if it’s scanning for dependency info, it should only be scanning package.json files that can possibly install them (at the root of packages), and also because anything scanning third-party code should never be able to assume it’s well-formed. Otherwise, it’d be a pretty trivial attack to add a malformed nested package.json file to a malicious package.

Yes, i could probably mock things out rather than have real fixtures, but then I’ve made my tests more brittle only so other tools’ bugs and security vulnerabilities can avoid getting fixed.

@fernandopasik
Copy link

fernandopasik commented Jan 25, 2022

Found this problem as well when running flow (https://github.com/flowtype/flow-bin) (latest version)

Error ┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ node_modules/resolve/test/resolver/malformed_package_json/package.json:2:1

Unexpected end of input, expected the token }

     1│ {
     2│

Found 1 error
error Command failed with exit code 2.

As a workaround I just added the file to the ignore list in the flowconfig file

[ignore]
<PROJECT_ROOT>/node_modules/resolve/test/resolver/malformed_package_json/package.json

@robertfoobar
Copy link

robertfoobar commented Jan 31, 2022

This issue unfortunately seems to cause a lot of avoidable hassle. It cost us almost one day to analyse.

Good npm citizens publish their tests, in my opinion.

I also don't agree. Shipping test code is considered a weakness in software development. There's even a CWE for that:

Accessible test applications can pose a variety of security risks. Since developers or administrators rarely consider that someone besides themselves would even know about the existence of these applications, it is common for them to contain sensitive information or functions.

Although you're IMO right that affected third party apps should be implemented more resiliently, this probably is not going to happen soon.

@ljharb
Copy link
Member

ljharb commented Jan 31, 2022

@robertfoobar it's not really possible to have "sensitive information or functions" in test files for a language like javascript; that CWE doesn't apply here.

@robertfoobar
Copy link

@ljharb That's not the point. My point is that this approach violates general best practices, namely

Remove test code before deploying the application into production.

and, in doing so, causes additional problems for others. 😕

@ljharb
Copy link
Member

ljharb commented Jan 31, 2022

@robertfoobar that's not been a best practice in the npm ecosystem, historically.

What I see is that it's not caused problems, it's exposed flaws in existing tools - flaws that have made them run MUCH slower than they need to, because they're scanning WAY more files than they need to.

@alansikora
Copy link

alansikora commented Feb 8, 2022

Having this file, named package.json, inside a test directory, on a dist build made me dig through for 2 days, I was on a very specific env.

Test code on dist versions does not make sense, I trust the authors already tested it when it was shipped.

@ljharb
Copy link
Member

ljharb commented Feb 8, 2022

@alansikora node versions are released after it's shipped, and nothing's tested on every possible runtime environment. deps shipping the tests means i can easily make sure those deps are behaving as expected, even on my idiosyncratic setups.

@EvHaus
Copy link

EvHaus commented Apr 29, 2022

@ngraef because npm explore foo && npm install && npm test should always work.
Good npm citizens publish their tests, in my opinion.

@ljharb I'd love to understand your position on this a bit better. I can I see the benefit in the sense that users could run tests against the package locally to get confidence of its code quality, but it seems like it comes with some serious tradeoffs, namely increased bundle size and issues like this one. In my view it's very easy to get the tests for the package by cloning the repo instead. I would imagine the number of people who would prefer a small npm bundle size would be much higher than the number of people who would appreciate tests to be included. Am I missing some other angle?

@ljharb
Copy link
Member

ljharb commented Apr 29, 2022

It has zero impact on bundle size, since only imported files end up in the bundle. Issues like this only happen when tools do incorrect things - namely, looking at package.json filed naively instead of only directly inside package boundaries.

The only impact it has (módulo broken tools) is on install size, and very little on that.

@LeviBroadnax
Copy link

This was absolutely hilarious, spent half an hour on it. Thanks for the laughs, added to my comedy gold bookmarks 👍🏽

@humarkx
Copy link

humarkx commented Jul 27, 2022

failed to parse node_modules/resolve/test/resolver/malformed_package_json/package.json
failed to parse node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/malformed_package_json/package.json

"eslint-plugin-react": "^7.30.1"

@ljharb
Copy link
Member

ljharb commented Jul 28, 2022

@humarkx whatever's trying to parse that is broken. I assume it's meteor since you're posting on this issue, but either way this repo isn't the place to post about it. See upthread.

@mirek
Copy link

mirek commented Mar 19, 2023

vscode with flow extension fails with "Unexpected end of input, expected the token }" as it digs into this test file.

Just drop test from being published in npm, tests should live in source control (github) not in published package.

@ljharb
Copy link
Member

ljharb commented Mar 19, 2023

@mirek why would vscode - or any tool - ever be digging into test files inside node_modules?

Tests should always be in the published package, so that npm install x && npm explore x && npm install && npm test always works.

@spflinux
Copy link

spflinux commented Apr 1, 2023

Hope to fix this problem earlier, this problem due to just one "{" in the package.json and can not be parse correctly.

@JakeThurman
Copy link

My org uses (Sonatype)[https://www.sonatype.com/products/sonatype-repository-firewall] for scanning dependencies for vulnerabilities. It parses package.json files under node-modules during this process to confirm what packages are going to be in play.

It fails reading node_modules\resolve\test\resolver\malformed_package_json\package.json. I'm able to ignore all errors but not this one specifically, and I'd rather not have to in case a new error appeared that I did want to address.

@ljharb I would really like to push back on the need to run tests on a dependency installed from npm. Maybe I am missing the use case, but clearly the current state is causing the lot of pain. Webpack used to have this problem from a similar tests and I filed a bug report, and they fixed it! Webpack does not ship their tests.

Since I do see you feel strongly, would you consider ignoring just the test/resolver/malformed_package_json directory from the package? That is the test case that causes issues with many scanners and may be a good compromise.

@ljharb
Copy link
Member

ljharb commented May 5, 2023

Then that’s a bug in sonatype; I’d suggest filing it with them.

no, i won’t ignore that test case in the package. I see this “pain” as a good thing since it’s exposed how many scanning tools have fundamentally broken and naive heuristics.

@JakeThurman
Copy link

@ljharb The check against all installed files is legit. Because you ship the test files, I imagine it ends up directly on many production servers, or at least is using during the build of production code, which we want to be safe as well. While it may seem like it at first, this is not naive.

The scanner checks EVERY file that is part of our production build for things like source and similarity to known malicious code.
Just because it's labeled as "tests", that is not a reason to trust it. How many zero day vulnerabilities are due to not starting from a point of zero-trust? To make up an example, for all we know this could be a binary for a virus called "package.json" to avoid detection, by being under tests/, being a "known safe file name", etc.

Just to emphasize, it's not that this file is malicious, but we have no reason to trust it until we check it.

Am I missing something about what makes checking all installed naive beyond that? I am open to changing my mind here but I'm just not understanding why we wouldn't check all code we install from the internet. Is there some other heuristic you are expecting?

@ljharb
Copy link
Member

ljharb commented May 5, 2023

The contents of a file that is never accessed, accessible, or executed is irrelevant.

Separately, is sonatype checking all JSON files? or is it merely checking package.json files? If the former, then a malformed JSON file shouldn't kill the overall check; if the latter, this isn't a package's package.json, so it shouldn't be checked at all.

@JakeThurman
Copy link

The contents of a file that is never accessed, accessible, or executed is irrelevant.

While true, to know what files will be executed at runtime is a hard problem to solve, no?

Separately, is sonatype checking all JSON files? or is it merely checking package.json files? If the former, then a malformed JSON file shouldn't kill the overall check; if the latter, this isn't a package's package.json, so it shouldn't be checked at all.

To my knowledge, all package.json files. And it does because you can import this sub-package using require("resolve/test/resolver/malformed_package_json"). Just in case we do, it gets scanned.

@ljharb
Copy link
Member

ljharb commented May 5, 2023

No, it's not a hard problem to solve - it's the way bundlers work. You can simply traverse the dependency graph.

It's a JSON file. It's literally impossible to have any malice coming from it, because all it can do is parse, or fail to parse. The only thing that makes "package.json" special is when it's at a package boundary, which this is not. I would be hesitant to trust a scanning tool that failed to understand this.

@JakeThurman
Copy link

JakeThurman commented May 5, 2023

You can simply traverse the dependency graph.

It does! But like I mentioned this is a subpackage of a package that is imported. So as far as the package tree is concerned, this is included.

It's a JSON file. It's literally impossible to have any malice coming from it, because all it can do is parse, or fail to parse. The only thing that makes "package.json" special is when it's at a package boundary, which this is not. I would be hesitant to trust a scanning tool that failed to understand this.

Subpackages are importable still though. Here's an article talking about them. And from a vulnerability perspective, it would leave a hole in the protection of the scanner if it just allowed anything to be in subpackages.

To be clear it's using the package.json file as one of many inputs to evaluation. It doesn't need it to be there and you can ignore parsing errors, but I would rather not so that I see any other issues that come up. Which, seems to be the concern of others on this thread as well


I also just want to take a moment to thank you for your work on this package. I know that Open Source development can be pretty thankless, and I just want to say I appreciate the time and care put into these packages that make the whole ecosystem better for everyone.

@ljharb
Copy link
Member

ljharb commented May 5, 2023

Subpackages surely can be imported, but they can't run arbitrary scripts or contain dependencies, which are the only security concerns to be worried about.

In particular, if a package.json file not at a package boundary doesn't parse, then node will either block importing or use fallback behavior as if it wasn't present - so a non-parseable package.json is the same as it not existing in the first place.

In other words, for a security scanner, a nonparseable package.json MUST always be ignored, or else it won't be checking for things the same way node does.

@JakeThurman
Copy link

JakeThurman commented May 5, 2023

Subpackages surely can be imported, but they can't run arbitrary scripts or contain dependencies, which are the only security concerns to be worried about.

While true, we also want to produce a full "bill of materials" for our product, including all open source packages used (this is one of the things the scanner outputs and it uses the package.json to get the name, urls, etc.). The scanner checks a CVE database for each package it finds on a variety of keys.

A subpackage may very well be an inlined vulnerable or malicious package and we want to scan that all the same. Not just by root package name.

In particular, if a package.json file not at a package boundary doesn't parse, then node will either block importing or use fallback behavior as if it wasn't present - so a non-parseable package.json is the same as it not existing in the first place.

In other words, for a security scanner, a nonparseable package.json MUST always be ignored, or else it won't be checking for things the same way node does.

Thanks for explaining. I think maybe this is the bit I was missing. I still disagree about shipping the tests, but I will add some more information to my bug against the scanner.

I'll leave it here. Thanks for engaging!

@ljharb
Copy link
Member

ljharb commented May 5, 2023

An SBOM that goes more granular than "a root package.json and its deps" is unnecessary, and one that includes unused code wouldn't be accurate anyways. A "subpackage" is just a subdirectory of a package, and in the npm ecosystem "package" is the atom - there's no such thing as a "subpackage" in reality, it's just a mental model one can hold when working with a package.

Thanks, I look forward to the link so I can follow progress.

@JakeThurman
Copy link

An SBOM that goes more granular than "a root package.json and its deps" is unnecessary, and one that includes unused code wouldn't be accurate anyways.

Even if it's unused, serving a malicious file from our domain by it being on our webserver would be bad. It's not that there aren't other mitigations to this, but we want defense in depth, and not allowing the file to make it to the webserver in the first place is our goal.

@ljharb
Copy link
Member

ljharb commented May 8, 2023

Well sure, but blindly serving node_modules is always a massive security hole, and should never be done in the first place.

@axiac
Copy link

axiac commented Oct 9, 2023

Tests should always be in the published package, so that npm install x && npm explore x && npm install && npm test always works.

I saw this statement repeated several times in this thread. Where does this comes from? Is it recommended somewhere in the Npm documentation?

It has zero impact on bundle size, since only imported files end up in the bundle.

This is an opinion strongly focused on frontend. The backend applications do not use bundles; bundling do not bring any benefit to them. These tests use space, trigger security issues and all the annoyances discussed here and do not bring any benefit.

The contents of a file that is never accessed, accessible, or executed is irrelevant.

This statement cannot be proved without running the code and taking all possible paths. The security scanning tools are correct. All files included in the application must be checked.

@ljharb
Copy link
Member

ljharb commented Oct 9, 2023

@axiac it was the way the entire community did it in the early days of npm, and some of us still cleave to the old ways.

Disk space is infinite and free, and any security scanner that reports on never-executed files is not one I'd suggest using.

The security scanning tools are thus useless because that is exactly what's required - what they're doing now is providing false positives, not actual results, which is the problem you're complaining about.

@ljharb ljharb closed this as completed in 39ed3c4 Oct 10, 2023
@axiac
Copy link

axiac commented Oct 24, 2023

@ljharb Thank you.

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

Successfully merging a pull request may close this issue.