1
0
mirror of https://github.com/verdaccio/verdaccio.git synced 2025-02-21 07:29:37 +01:00

Merge remote-tracking branch 'origin/master' into 4.1.x

This commit is contained in:
Juan Picado @jotadeveloper 2019-06-13 22:04:35 +02:00
commit 0fa26293a8
No known key found for this signature in database
GPG Key ID: 15AA875EF3768142
24 changed files with 567 additions and 141 deletions

@ -14,4 +14,5 @@ Dockerfile
*.scss
*.png
*.jpg
*.sh
test/unit/partials/

@ -2,6 +2,46 @@
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
## [4.0.2](https://github.com/verdaccio/verdaccio/compare/v4.0.1...v4.0.2) (2019-06-13)
### Bug Fixes
* correctly check if the proxy setting evaluates to false ([#1336](https://github.com/verdaccio/verdaccio/issues/1336)) ([df834f4](https://github.com/verdaccio/verdaccio/commit/df834f4))
* update dependencies ([e581634](https://github.com/verdaccio/verdaccio/commit/e581634)), closes [#1339](https://github.com/verdaccio/verdaccio/issues/1339)
* update security policy details ([#1342](https://github.com/verdaccio/verdaccio/issues/1342)) ([ddcd89d](https://github.com/verdaccio/verdaccio/commit/ddcd89d))
* **api:** force authenticate on login ([#1347](https://github.com/verdaccio/verdaccio/issues/1347)) ([85c1bd1](https://github.com/verdaccio/verdaccio/commit/85c1bd1))
* **ui:** failed to load all packages after login ([192fb77](https://github.com/verdaccio/verdaccio/commit/192fb77))
## [4.0.1](https://github.com/verdaccio/verdaccio/compare/v4.0.0...v4.0.1) (2019-05-28)
### Bug Fixes
* **web:** fix sidebar tarball protocol ([#1326](https://github.com/verdaccio/verdaccio/issues/1326)) ([de04463](https://github.com/verdaccio/verdaccio/commit/de04463)), closes [#1320](https://github.com/verdaccio/verdaccio/issues/1320)
# [4.0.0](https://github.com/verdaccio/verdaccio/compare/v4.0.0-beta.8...v4.0.0) (2019-05-26)
### Bug Fixes
* add missing pkg version and name on start up ([8cf3966](https://github.com/verdaccio/verdaccio/commit/8cf3966))
* update @verdaccio/ui-theme:0.1.7 ([8e48eea](https://github.com/verdaccio/verdaccio/commit/8e48eea))
* warning text is hard to read when running under root ([3ac038f](https://github.com/verdaccio/verdaccio/commit/3ac038f))
### Features
* create security policy ([#1322](https://github.com/verdaccio/verdaccio/issues/1322)) ([0e9f23d](https://github.com/verdaccio/verdaccio/commit/0e9f23d))
* prepare release v4 ([#1307](https://github.com/verdaccio/verdaccio/issues/1307)) ([b9506d6](https://github.com/verdaccio/verdaccio/commit/b9506d6))
* update readme v4 ([#1312](https://github.com/verdaccio/verdaccio/issues/1312)) ([7686417](https://github.com/verdaccio/verdaccio/commit/7686417))
# [4.0.0-beta.10](https://github.com/verdaccio/verdaccio/compare/v4.0.0-beta.9...v4.0.0-beta.10) (2019-05-20)

@ -1,6 +1,6 @@
![verdaccio logo](https://github.com/verdaccio/verdaccio/raw/master/assets/bitmap/verdaccio%402x.png)
![verdaccio logo](https://cdn.verdaccio.dev/readme/verdaccio@2x.png)
![verdaccio gif](https://user-images.githubusercontent.com/558752/52916111-fa4ba980-32db-11e9-8a64-f4e06eb920b3.png)
![verdaccio gif](https://cdn.verdaccio.dev/readme/readme-website.png)
# Version 4
@ -14,8 +14,7 @@ Google Cloud Storage** or create your own plugin.
[![verdaccio (latest)](https://img.shields.io/npm/v/verdaccio/latest.svg)](https://www.npmjs.com/package/verdaccio)
[![verdaccio (next)](https://img.shields.io/npm/v/verdaccio/next.svg)](https://www.npmjs.com/package/verdaccio)
[![verdaccio (next)](http://img.shields.io/npm/dy/verdaccio.svg)](https://www.npmjs.com/package/verdaccio)
[![verdaccio (downloads)](http://img.shields.io/npm/dy/verdaccio.svg)](https://www.npmjs.com/package/verdaccio)
[![docker pulls](https://img.shields.io/docker/pulls/verdaccio/verdaccio.svg?maxAge=43200)](https://verdaccio.org/docs/en/docker.html)
[![backers](https://opencollective.com/verdaccio/tiers/backer/badge.svg?label=Backer&color=brightgreen)](https://opencollective.com/verdaccio)
[![stackshare](https://img.shields.io/badge/Follow%20on-StackShare-blue.svg?logo=stackshare&style=flat)](https://stackshare.io/verdaccio)
@ -82,7 +81,7 @@ $ npm set registry http://localhost:4873/
Now you can navigate to [http://localhost:4873/](http://localhost:4873/) where your local packages will be listed and can be searched.
> Warning: Verdaccio does not currently support PM2's cluster mode, running it with cluster mode may cause unknown behavior.
> Warning: Verdaccio [does not currently support PM2's cluster mode](https://github.com/verdaccio/verdaccio/issues/1301#issuecomment-489302298), running it with cluster mode may cause unknown behavior.
## Publishing
@ -120,7 +119,7 @@ docker pull verdaccio/verdaccio
Available as [tags](https://hub.docker.com/r/verdaccio/verdaccio/tags/).
```
docker pull verdaccio/verdaccio:4.0.0
docker pull verdaccio/verdaccio:4
```
### Running verdaccio using Docker
@ -153,6 +152,7 @@ Verdaccio aims to support all features of a standard npm client that make sense
- Registering new users (npm adduser {newuser}) - **supported**
- Change password (npm profile set password) - **supported**
- Transferring ownership (npm owner add {user} {pkg}) - not supported, *PR-welcome*
- Token (npm token) - wip [#1271](https://github.com/verdaccio/verdaccio/pull/1271)
### Miscellany
@ -162,7 +162,7 @@ Verdaccio aims to support all features of a standard npm client that make sense
### Security
- npm audit - **supported**
- npm/yarn audit - **supported**
## Report a vulnerability
@ -182,24 +182,22 @@ You can find and chat with then over Discord, click [here](http://chat.verdaccio
## Who is using Verdaccio?
* [create-react-app](https://github.com/facebook/create-react-app/blob/master/CONTRIBUTING.md#contributing-to-e2e-end-to-end-tests) *(+64k ⭐️)*
* [Storybook](https://github.com/storybooks/storybook) *(+34k ⭐️)*
* [Gatsby](https://github.com/gatsbyjs/gatsby) *(+31k ⭐️)*
* [Uppy](https://github.com/transloadit/uppy) *(+15k ⭐️)*
* [Aurelia Framework](https://github.com/aurelia) *(+10k ⭐️)*
* [create-react-app](https://github.com/facebook/create-react-app/blob/master/CONTRIBUTING.md#contributing-to-e2e-end-to-end-tests) *(+67k ⭐️)*
* [Storybook](https://github.com/storybooks/storybook) *(+37k ⭐️)*
* [Gatsby](https://github.com/gatsbyjs/gatsby) *(+34k ⭐️)*
* [Uppy](https://github.com/transloadit/uppy) *(+19k ⭐️)*
* [Aurelia Framework](https://github.com/aurelia) *(+11k ⭐️)*
* [bit](https://github.com/teambit/bit) *(+6k ⭐️)*
* [pnpm](https://github.com/pnpm/pnpm) *(+5k ⭐️)*
* [Mozilla Neutrino](https://github.com/neutrinojs/neutrino) *(+3k ⭐️)*
* [Amazon Web Services Cloud Development Kit](https://github.com/awslabs/aws-cdk) *(+1.8k ⭐️)*
* [Hyperledger Composer](https://github.com/hyperledger/composer) *(+1.6k ⭐️)*
* [webiny-js](https://github.com/Webiny/webiny-js) *(+1k ⭐️)*
## Special Thanks
🤓 Don't be shy, you also can be in [the list](https://github.com/verdaccio/website/blob/master/docs/who-is-using.md).
Thanks to the following companies to help us to achieve our goals providing free open source licenses.
## Sponsorship
[![jetbrain](assets/thanks/jetbrains/logo.png)](https://www.jetbrains.com/)
[![crowdin](assets/thanks/crowdin/logo.png)](https://crowdin.com/)
[![balsamiq](assets/thanks/balsamiq/logo.jpg)](https://balsamiq.com/)
If you are a *company/project* and you 😍 Verdaccio and FOSS, your **logo can be here** 😉 if you support our activities. [Donate](https://opencollective.com/verdaccio).
## Open Collective Sponsors
@ -222,6 +220,14 @@ Thank you to all our backers! 🙏 [[Become a backer](https://opencollective.com
[![backers](https://opencollective.com/verdaccio/backers.svg?width=890)](https://opencollective.com/verdaccio#backers)
## Special Thanks
Thanks to the following companies to help us to achieve our goals providing free open source licenses.
[![jetbrain](assets/thanks/jetbrains/logo.png)](https://www.jetbrains.com/)
[![crowdin](assets/thanks/crowdin/logo.png)](https://crowdin.com/)
[![balsamiq](assets/thanks/balsamiq/logo.jpg)](https://balsamiq.com/)
## Contributors
This project exists thanks to all the people who contribute. [[Contribute](CONTRIBUTING.md)].
@ -232,15 +238,15 @@ This project exists thanks to all the people who contribute. [[Contribute](CONTR
If you have any issue you can try the following options, do no desist to ask or check our issues database, perhaps someone has asked already what you are looking for.
* [Blog](https://medium.com/verdaccio)
* [Blog](https://verdaccio.org/blog/)
* [Donations](https://opencollective.com/verdaccio)
* [Roadmaps](https://github.com/verdaccio/verdaccio/projects)
* [Reporting an issue](https://github.com/verdaccio/verdaccio/blob/master/CONTRIBUTING.md#reporting-a-bug)
* [Running discussions](https://github.com/verdaccio/verdaccio/issues?q=is%3Aissue+is%3Aopen+label%3Adiscuss)
* [Chat](http://chat.verdaccio.org/)
* [Logos](https://verdaccio.org/docs/en/logo)
* [FAQ](https://github.com/verdaccio/verdaccio/issues?utf8=%E2%9C%93&q=is%3Aissue%20label%3Aquestion%20)
* [Docker Examples](https://github.com/verdaccio/docker-examples)
* [FAQ](https://github.com/verdaccio/verdaccio/issues?utf8=%E2%9C%93&q=is%3Aissue%20label%3Aquestion%20)
### License

@ -1,30 +1,73 @@
# Security Policy
## Supported Versions
## Supported versions
Use this section to tell people about which versions of your project are
currently being supported with security updates.
The following table describes the versions of this project that are currently supported with security updates:
| Version | Supported |
| ------- | ------------------ |
| 2.x | :x: |
| 3.x | :white_check_mark: |
| 4.x | :white_check_mark: |
| 2.x | :x: |
| 3.x | :white_check_mark: |
| 4.x | :white_check_mark: |
## Reporting a Vulnerability
## Responsible disclosure security policy
At Verdaccio, we consider the security of our systems a top priority. But no matter how much effort we put into system security, there can still be vulnerabilities present. If you've discovered a vulnerability, please follow the guidelines below to report it to our team:
A responsible disclosure policy helps protect users of the project from publicly disclosed security vulnerabilities without a fix by employing a process where vulnerabilities are first triaged in a private manner, and only publicly disclosed after a reasonable time period that allows patching the vulnerability and provides an upgrade path for users.
* Report it either [Snyk Security Team](https://snyk.io/vulnerability-disclosure/) or [npmjs Security Team](https://www.npmjs.com/advisories/report?package=verdaccio), they will be in contact with us in case of confirming the vulnerability.
* E-mail your findings to [verdaccio@pm.me](mailto:verdaccio@pm.me). If the report contains highly sensitive information, please consider encrypting your findings using our [PGP key](https://verdaccio.nyc3.digitaloceanspaces.com/gpg/publickey.verdaccio@pm.me.asc).
When contacting us directly via email, we will do our best efforts to respond in a reasonable time to resolve the issue. When contacting a security program their disclosure policy will provide details on timeframe, processes and paid bounties.
Please follow these rules when testing/reporting vulnerabilities:
* Do not take advantage of the vulnerability you have discovered, for example by downloading more data than is necessary to demonstrate the vulnerability.
* Do not read, modify or delete data that isn't your own.
* We ask that you do not disclose the findings to third parties until it has been resolved.
We kindly ask you to refrain from malicious acts that put our users, the project, or any of the projects team members at risk.
What we promise:
* We will respond to your report within 3 business days with our evaluation of the report and an expected resolution date.
* We will keep you informed during all stages of resolving the problem.
* To show our appreciation for your effort and cooperation during the report, we will list your name and a link to a personal website/social network profile on the page below so that the public can know you've helped keep Verdaccio secure.
## Reporting a security issue
At Verdaccio, we consider the security of our systems a top priority. But no matter how much effort we put into system security, there can still be vulnerabilities present.
If you discover a security vulnerability, please use one of the following means of communications to report it to us:
* Report the security issue to the Node.js Security WG through the [HackerOne program](https://hackerone.com/nodejs-ecosystem) for ecosystem modules on npm, or to [Snyk Security Team](https://snyk.io/vulnerability-disclosure). They will help triage the security issue and work with all involved parties to remediate and release a fix.
Note that time-frame and processes are subject to each programs own policy.
* Report the security issue to the project maintainers directly at verdaccio@pm.me. If the report contains highly sensitive information, please be advised to encrypt your findings using our [PGP key](https://verdaccio.nyc3.digitaloceanspaces.com/gpg/publickey.verdaccio@pm.me.asc) which is also available in this document.
Your efforts to responsibly disclose your findings are sincerely appreciated and will be taken into account to acknowledge your contributions.
## PGP key
The following is this projects PGP key which should be used to encrypt any sensitive information shared on unsecured medium such as e-mails:
```
-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: OpenPGP.js v4.5.1
Comment: https://openpgpjs.org
xsBNBFzm3asBCACxnJDv1r6dxiM2e8iqS6B7fxY2I3X1Rc+3m8mhXOwVwRG4
AOrQ417oSzsVLf4iocg+DWrtxzY79odTLJEovVt79rxwqIIl4y96tH+29kLB
ao7eaYZacfstonVkBAmxBLaYv1x7cqWuukm6sBCOxapW1X9BcbR3vOghDziY
/1AwNjupAOPvKNMtghjrdh3w0iMfZS1hw28zjM1oCeezEil+CTjgQDN+69qS
UFG/BInJ7CVn9TvhU85inSwpxVa576fkhvFoNUrGvFvYRWtXRJndbRdBodVj
C9At/Gb2IeNf7xqXH2KloZ1yaVNVSzLX4jqrMWeF+9Z12SjUyL6G9TwDABEB
AAHNIXZlcmRhY2Npb0BwbS5tZSA8dmVyZGFjY2lvQHBtLm1lPsLAdQQQAQgA
HwUCXObdqwYLCQcIAwIEFQgKAgMWAgECGQECGwMCHgEACgkQpSvoGbwFJYhn
2wf+JF+yLQXh1EFMih6lpbx243hvglgOWmcigYVRh5mSfULcdW2pmkPQXqhE
DW73qqwN9G9piiPnGMw7sKoB7XJVuFKyvHOYKtem5UQVRvs2rTxnSc5qFcUJ
0w3Tw/pZ9B3fYAEYti2B/GsSOzaECfBKCFOg15xXGAdwfgff5FsorN1Gb6MG
eCO9c8faSF/+fQUCfokwMDVzxXQFZEMx3q/rHVJ/Fm+XelZ+00c9fdyiuPW5
dM9gATle7lz0iPtxaUDGLW8QZ/7b6O8IJ1kle0tL4AE++bXsVWxNdzhlNohH
Hn09sIdFnG4ySTz4YJjiDd70ZdQjOGEGvutymEIN1xcNq87ATQRc5t2rAQgA
yX2ZhUCtrz7lzK0992yveB+duVF//yo9Pei2ra9Z3GNmA+oWlRH1FTWpAmVH
uDdUchTnxAwaKntabt3Mb1AgEZwrdiG4LuHFbdx2ls93BJ5lXdp7vB6pVf3N
IrhHKyQ/Y5L5kMSj/GjrhO19zmj6mPPEgb3M3ZIZjQUF4pro0pExuAPA9Wxe
awn5+0BUYFs4mZQDtTdiVuz5tWA0fNtt1aBfOPA97tmn18y4b1b0iQIJQpep
BVVnFLeAZOevDcBJFbmQOdAjufWSSgpzX+FZ3rx6RVwwKxUiVQyUuwSQkKh5
RufZ5zE0y7Fe/YlWXbKoj4zNJqYtjPSPngQRWf7UpwARAQABwsBfBBgBCAAJ
BQJc5t2rAhsMAAoJEKUr6Bm8BSWIoYQH+QDw0Z84tZK4N1lh49hYyohs6vNU
9kG69nKLQA5NymPtTxh8YOJhdJL697FkvKI4OGEO2FXUmcJS3CBJ2nBVKMq2
1biDRKC4OhIU2RgFhS6bHy6VOn24EYs77T+zX8YXpz8ulYVln2b0QZCubN0Z
L50tEC8HnuVMVN+/pqITdD3FjzwGZgHdW8qkKgD6qhObHCl8/cW2buCsaIAY
eZWVPgPY1S1U0V608qYNtUCkrmUW5Sl6YLvz7JTvTsaym5mzyFXF3ErAURgI
/v4XaWmRgNGIxbIxsFGuEs+KIKBQDJmtvJCVpBNS5IYnFf5h/LA5cfkwMKJt
wXhyE0b/iDs60ZM=
=QWXs
-----END PGP PUBLIC KEY BLOCK-----
```

@ -2,4 +2,9 @@
## we highly recommend either visit
## https://verdaccio.org/docs/en/configuration
## or read the local file
## docs/config.md
## https://github.com/verdaccio/website/tree/master/docs/config.md
## contribute with translations
## You can contribute translating documentation through the crowdin platform
## https://crowdin.com/project/verdaccio

@ -5,7 +5,7 @@ module.exports = {
verbose: true,
collectCoverage: true,
testURL: 'http://localhost',
testRegex: '(test/unit.*\\.spec|test/unit/webui/.*\\.spec)\\.js',
testRegex: '(test/unit.*\\.spec)\\.js',
// Some unit tests rely on data folders that look like packages. This confuses jest-hast-map
// when it tries to scan for package.json files.
modulePathIgnorePatterns: [

@ -1,6 +1,6 @@
{
"name": "verdaccio",
"version": "4.0.0-beta.10",
"version": "4.0.2",
"description": "A lightweight private npm proxy registry",
"author": {
"name": "Verdaccio Maintainers",
@ -16,18 +16,18 @@
},
"dependencies": {
"@verdaccio/local-storage": "2.1.0",
"@verdaccio/streams": "2.0.0",
"@verdaccio/readme": "1.0.3",
"@verdaccio/ui-theme": "0.1.10",
"@verdaccio/streams": "2.0.0",
"@verdaccio/ui-theme": "0.1.11",
"JSONStream": "1.3.5",
"async": "3.0.1-0",
"async": "3.0.1",
"body-parser": "1.19.0",
"bunyan": "1.8.12",
"commander": "2.20.0",
"compression": "1.7.4",
"cookies": "0.7.3",
"cors": "2.8.5",
"dayjs": "1.8.13",
"dayjs": "1.8.14",
"express": "4.16.4",
"handlebars": "4.1.2",
"http-errors": "1.7.2",
@ -37,13 +37,13 @@
"lodash": "4.17.11",
"lunr-mutable-indexes": "2.3.2",
"marked": "0.6.2",
"mime": "2.4.2",
"mime": "2.4.4",
"minimatch": "3.0.4",
"mkdirp": "0.5.1",
"mv": "2.1.1",
"pkginfo": "0.4.1",
"request": "2.87.0",
"semver": "6.0.0",
"semver": "6.1.1",
"verdaccio-audit": "1.2.0",
"verdaccio-htpasswd": "2.0.0"
},
@ -73,6 +73,7 @@
"jest": "24.7.1",
"jest-environment-node": "24.7.1",
"lint-staged": "8.1.5",
"nock": "10.0.6",
"prettier": "1.17.0",
"puppeteer": "1.8.0",
"rimraf": "2.6.3",

@ -10,4 +10,4 @@ echo "Bumping version to new tag: ${lastTag}"
echo "//$REGISTRY_URL/:_authToken=$REGISTRY_AUTH_TOKEN" > .npmrc
# Publish to NPM
npm publish --tag next --registry https://$REGISTRY_URL/
npm publish --registry https://$REGISTRY_URL/

@ -8,9 +8,10 @@ import Cookies from 'cookies';
import { ErrorCode } from '../../../lib/utils';
import { API_ERROR, API_MESSAGE, HTTP_STATUS } from '../../../lib/constants';
import { createSessionToken, getApiToken, getAuthenticatedMessage, validatePassword } from '../../../lib/auth-utils';
import { createRemoteUser, createSessionToken, getApiToken, getAuthenticatedMessage, validatePassword } from '../../../lib/auth-utils';
import logger from '../../../lib/logger';
import type { Config } from '@verdaccio/types';
import type { Config, RemoteUser } from '@verdaccio/types';
import type { $Response, Router } from 'express';
import type { $RequestExtend, $ResponseExtend, $NextFunctionVer, IAuth } from '../../../../types';
@ -22,17 +23,26 @@ export default function(route: Router, auth: IAuth, config: Config) {
});
});
route.put('/-/user/:org_couchdb_user/:_rev?/:revision?', async function(req: $RequestExtend, res: $Response, next: $NextFunctionVer) {
route.put('/-/user/:org_couchdb_user/:_rev?/:revision?', function(req: $RequestExtend, res: $Response, next: $NextFunctionVer) {
const { name, password } = req.body;
const remoteName = req.remote_user.name;
if (_.isNil(req.remote_user.name) === false) {
const token = name && password ? await getApiToken(auth, config, req.remote_user, password) : undefined;
if (_.isNil(remoteName) === false && _.isNil(name) === false && remoteName === name) {
auth.authenticate(name, password, async function callbackAuthenticate(err, groups) {
if (err) {
logger.logger.trace({ name, err }, 'authenticating for user @{username} failed. Error: @{err.message}');
return next(ErrorCode.getCode(HTTP_STATUS.UNAUTHORIZED, API_ERROR.BAD_USERNAME_PASSWORD));
}
res.status(HTTP_STATUS.CREATED);
const restoredRemoteUser: RemoteUser = createRemoteUser(name, groups);
const token = await getApiToken(auth, config, restoredRemoteUser, password);
return next({
ok: getAuthenticatedMessage(req.remote_user.name),
token,
res.status(HTTP_STATUS.CREATED);
return next({
ok: getAuthenticatedMessage(req.remote_user.name),
token,
});
});
} else {
if (validatePassword(password) === false) {

@ -2,9 +2,8 @@
* @prettier
* @flow
*/
import _ from 'lodash';
import { addScope, addGravatarSupport, deleteProperties, sortByName, parseReadme, formatAuthor } from '../../../lib/utils';
import { addScope, addGravatarSupport, deleteProperties, sortByName, parseReadme, formatAuthor, convertDistRemoteToLocalTarballUrls } from '../../../lib/utils';
import { allow } from '../../middleware';
import { DIST_TAGS, HEADER_TYPE, HEADERS, HTTP_STATUS } from '../../../lib/constants';
import { generateGravatarUrl } from '../../../utils/user';
@ -102,7 +101,8 @@ function addPackageWebApi(route: Router, storage: IStorageHandler, auth: IAuth,
callback: function(err: Error, info: $SidebarPackage) {
if (_.isNil(err)) {
let sideBarInfo: any = _.clone(info);
sideBarInfo.latest = info.versions[info[DIST_TAGS].latest];
sideBarInfo.versions = convertDistRemoteToLocalTarballUrls(info, req, config.url_prefix).versions;
sideBarInfo.latest = sideBarInfo.versions[info[DIST_TAGS].latest];
sideBarInfo.latest.author = formatAuthor(sideBarInfo.latest.author);
sideBarInfo = deleteProperties(['readme', '_attachments', '_rev', 'name'], sideBarInfo);
if (config.web) {

@ -156,10 +156,6 @@ export const PACKAGE_ACCESS = {
ALL: '**',
};
export const UPDATE_BANNER = {
CHANGELOG_URL: 'https://github.com/verdaccio/verdaccio/releases/tag/',
};
export const STORAGE = {
PACKAGE_FILE_NAME: 'package.json',
FILE_EXIST_ERROR: 'EEXISTS',

@ -413,13 +413,13 @@ class Storage implements IStorageHandler {
returns callback(err, result, uplink_errors)
*/
_syncUplinksMetadata(name: string, packageInfo: Package, options: ISyncUplinks, callback: Callback): void {
let exists = true;
let found = true;
const self = this;
const upLinks = [];
const hasToLookIntoUplinks = _.isNil(options.uplinksLook) || options.uplinksLook;
if (!packageInfo) {
exists = false;
found = false;
packageInfo = generatePackageTemplate(name);
}
@ -491,14 +491,36 @@ class Storage implements IStorageHandler {
// if we got to this point, assume that the correct package exists
// on the uplink
exists = true;
found = true;
cb();
});
},
(err: Error, upLinksErrors: any) => {
assert(!err && Array.isArray(upLinksErrors));
if (!exists) {
return callback(ErrorCode.getNotFound(API_ERROR.NO_PACKAGE), null, upLinksErrors);
// Check for connection timeout or reset errors with uplink(s)
// (these should be handled differently from the package not being found)
if (!found) {
let uplinkTimeoutError;
for (let i = 0; i < upLinksErrors.length; i++) {
if (upLinksErrors[i]) {
for (let j = 0; j < upLinksErrors[i].length; j++) {
if (upLinksErrors[i][j]) {
const code = upLinksErrors[i][j].code;
if (code === 'ETIMEDOUT' || code === 'ESOCKETTIMEDOUT' || code === 'ECONNRESET') {
uplinkTimeoutError = true;
break;
}
}
}
}
}
if (uplinkTimeoutError) {
return callback(ErrorCode.getServiceUnavailable(), null, upLinksErrors);
} else {
return callback(ErrorCode.getNotFound(API_ERROR.NO_PACKAGE), null, upLinksErrors);
}
}
if (upLinks.length === 0) {

@ -551,7 +551,7 @@ class ProxyStorage implements IProxy {
// Otherwise misconfigured proxy could return 407:
// https://github.com/rlidwka/sinopia/issues/254
//
if (this.proxy === false) {
if (!this.proxy) {
headers['X-Forwarded-For'] = (req.headers['x-forwarded-for'] ? req.headers['x-forwarded-for'] + ', ' : '') + req.connection.remoteAddress;
}
}

@ -1,63 +0,0 @@
/**
* @prettier
* @flow
*/
import request from 'request';
import semver from 'semver';
import { red, green, blue, white, bold } from 'kleur';
import _ from 'lodash';
import { UPDATE_BANNER, DEFAULT_REGISTRY, HTTP_STATUS } from './constants';
const VERDACCIO_LATEST_REGISTRY_URL = `${DEFAULT_REGISTRY}/verdaccio/latest`;
/**
* Creates NPM update banner using kleur
*/
export function createBanner(currentVersion: string, newVersion: string, releaseType: string): string {
const changelog = `${UPDATE_BANNER.CHANGELOG_URL}v${newVersion}`;
const versionUpdate = `${bold().red(currentVersion)}${bold().green(newVersion)}`;
const banner = `
${white().bold('A new ' + _.upperCase(releaseType) + ' version of Verdaccio is available. ' + versionUpdate)}
${white().bold('Run ' + green('npm install -g verdaccio') + ' to update.')}
${white().bold('Registry: ' + DEFAULT_REGISTRY)}
${blue().bold('Changelog: ' + changelog)}
`;
return banner;
}
/**
* creates error banner
*/
export function createErrorBanner(message: string): string {
const banner = `
${red().bold('Unable to check verdaccio version on ' + DEFAULT_REGISTRY)}
${red().bold('Error: ' + message)}
`;
return banner;
}
/**
* Show verdaccio update banner on start
*/
export function verdaccioUpdateBanner(pkgVersion: string) {
request(VERDACCIO_LATEST_REGISTRY_URL, function(error: ?Object = null, response: Object = {}) {
if (!error && response.statusCode === HTTP_STATUS.OK && response.body) {
// In case, NPM does not returns version, keeping version equals to
// verdaccio version.
const { version = pkgVersion } = JSON.parse(response.body);
const releaseType = semver.diff(version, pkgVersion);
if (releaseType && semver.gt(version, pkgVersion)) {
const banner = createBanner(pkgVersion, version, releaseType);
/* eslint-disable-next-line */
console.log(banner);
}
} else {
const errorBanner = createErrorBanner(JSON.stringify(error));
/* eslint-disable-next-line */
console.log(errorBanner);
}
});
}

156
test/README.md Normal file

@ -0,0 +1,156 @@
## How to test Verdaccio
Welcome to the testing folder at Verdaccio. This document aims to help you understand how Verdaccio should be tested.
First of all, we should explain the testing frameworks being used. We use `jest` and other tools such as `supertest` to be able to test the API, and `puppeteer` for End-to-End testing.
We go along with the following rules in order to be consistent with all tests which will make your code review smooth and fast:
* We **type** all our tests. eg `const foo: number = 3;`
* **Each test should be as small as possible**: You should use the `test()` block to test only one thing and do not depend on other tests. If the test requires different steps, group them with a `describe()` block.
* All `test()` **headers titles** should begin with `test('should test ...')`: For consistency with reporting tools, this makes it easier to match the test with the feature needed to be tested.
* **Any mock data** should be located in the `partials` folder in each section.
* Use `yaml` for **configuration files examples** instead of JSON.
* If you use a **file based mock storage**, it should be located in the `store` folder in each section.
* All tests **MUST NOT** rely on external sources and must be able to run them **offline**.
* Tests **must run on the following Operating Systems**: Unix (Mac, Linux) and Windows (7 -> latest).
* If you are creating mock data file which use the state and need a clean state, use `rimraf` to remove folders.
## Testing sections
Verdaccio testing is split in 3 sections, each of them has different setup and scope. The most important is the **unit test**. All sections have custom **jest configuration files**.
If you are adding new tests, comply with the following:
* If you add a new API endpoint, unit and functional tests are mandatory.
* If you add a utility, unit test is mandatory.
* If you are adding a new web API endpoint, the unit test, functional test and if such endpoint has new changes in the UI, E2E test is also mandatory.
* If you add or refactor a core class, unit test is mandatory.
* If you fix a bug, you **must** add a new `test()` block to prove that the patch fixes the bug.
### Unit test
Unit tests aim to test the CLI API and the Web API. The configuration file is located at `jest.config.js`.
> Unit testing does not need require pre-compile code, jest will catch any change done to the `{root}/src` files.
#### Testing an endpoint
We have prepared a template at `test/unit/api/api.__test.template.spec.js` that you can follow to create your own unit test. Only the tests are appended with `.spec.js` which will be found and used by `jest`.
> Feel free to suggest improvements to the template, there is still a lot of room for improvement.
We recommend the following approach when you create a unit test:
* For new utilities, we recommend creating a new spec.
* For existing utilities, if the method is already being tested, just add a new `test()` block.
* Notice that all API spec files are appended with `api.[feature].spec.js`, we recommend to follow the same approach. eg: `api.[deprecate].spec.js`.
* Don't mix utilities with API tests.
### Functional tests
The functional tests aim to run only **cli endpoint** and **web point** using real request to an existing and compiled running Verdaccio server.
> Be aware if you change something in the `{root}/src` source code, you must run `yarn code:build` before to be able to see your changes because functional tests use the transpiled code.
All tests must be included in the `test/functional/index.spec.js` file, which bootstraps the whole process. There is only one spec file and **must be only one**.
The jest configuration file is defined in `test/jest.config.functional.js`. The configuration will create a custom environment launching 3 Verdaccio servers with different configurations.
The servers are linked as follows:
* Server 1
* -> Server 2
* -> Server 3
* Server 2
* -> Server 1
* Server 3
* -> Server 2
* -> Server 1
* Express app: (if you need to emulate any external endpoint, use the express app)
Server 1 runs on port `55551`, Server 2 on port `55552` and Server 3 on port `55553`.
> If you have the need to increase the number of servers running, it is possible, but please discuss with the team before you go in that path.
#### Adding a new block
To add a new feature you need to export the feature as a function that take as an argument any of the servers you want to interact.
```js
// newFeature.js
export default function(server) {
describe('package access control', () => {
test('should ...', (done) => {
done();
});
});
}
```
Then import the feature and run the function within the main `describe` block.
```js
// index.spec.js
import newFeature from './newFeature';
describe('functional test verdaccio', function() {
// test are fast, but do not change this time out, 10 seconds should be more than enough
jest.setTimeout(10000);
// servers are accessed via a global jest state.
const server1: IServerBridge = global.__SERVERS__[0];
const server2: IServerBridge = global.__SERVERS__[1];
const server3: IServerBridge = global.__SERVERS__[2];
const app = global.__WEB_SERVER__.app;
// include as much servers you need
newFeature(server1, server2, server3);
});
```
Functional tests run over one single file, thus, it is not possible at this stage to run tests individually.
### E2E Test
Verdaccio includes a Web User Interface that must be tested. We use End-to-End testing to run some smock tests against the web API using the UI Theme
include by default.
```bash
yarn lint && yarn test:all
```
The test does not have aim to test the integrity of the page, mostly, ensure the basic functionality still works. If you add or modify
a UI feature, the tests must be updated.
> The tests rely on CSS classes naming convention, so, it is required some sort of coordination with the **verdaccio/ui** project.
We uses `puppeteer`, you can find more information about how to use it in their website.
## Before commit
We recommend run your tests and linters before commit.
```bash
yarn lint && yarn test:all
```
You can find more in our [guide about run and debugging test](https://github.com/verdaccio/verdaccio/wiki/Running-and-Debugging-tests#running-the-test).
## Continuous Integration
Verdaccio uses [CircleCI](https://circleci.com/gh/verdaccio) as its primary Continuous Integration tool. We run the tests against the most common Node.js versions available. Among them is LTS and the latest release. Before the PR is being merged, all checks must be green.
Node.js versions available, LTS and the latest release. Before the PR is being merged, all check must be green.
> You need a CircleCI account to be able see the test running

@ -0,0 +1,21 @@
const nock = require('nock');
function Plugin(config) {
const self = Object.create(Plugin.prototype);
self._config = config;
return self;
}
Plugin.prototype.register_middlewares = function (app, auth, storage) {
app.get('/test-uplink-timeout-*', function (req, res, next) {
nock('http://localhost:55552')
.get(req.path)
.socketDelay(31000).reply(200); // 31s is greater than the default 30s connection timeout
next();
});
}
module.exports = Plugin;

@ -28,6 +28,7 @@ import race from './performance/race';
import pluginsAuth from './plugins/auth';
import middleware from './plugins/middleware';
import upLinkCache from './uplinks/cache';
import uplinkTimeout from './uplinks/timeout';
describe('functional test verdaccio', function() {
jest.setTimeout(10000);
@ -54,14 +55,15 @@ describe('functional test verdaccio', function() {
security(server1);
addtag(server1);
pluginsAuth(server2);
notify(app);
notify(app);
uplinkTimeout(server1, server2, server3);
// requires packages published to server1/server2
upLinkCache(server1, server2, server3);
adduser(server1);
logout(server1);
basic(server1, server2);
simpleSearch(server1, server2, app)
});
process.on('unhandledRejection', function(err) {

@ -6,6 +6,10 @@ web:
enable: true
title: verdaccio-server-1
middlewares:
../fixtures/plugins/middlewares.uplink:
message: provides uplink mocking (e.g. simulates socket timeout)
auth:
auth-memory:
users:
@ -116,6 +120,12 @@ packages:
publish: $authenticated
storage: sub_storage
'test-uplink-timeout-*':
access: $all
proxy:
- server2
- server3
'*':
access: test $anonymous
publish: test $anonymous

@ -35,6 +35,10 @@ packages:
access: $all
proxy: server2
'test-uplink-timeout-*':
access: $all
publish: $all
'*':
access: $all

@ -0,0 +1,27 @@
import {HTTP_STATUS} from "../../../src/lib/constants";
const PKG_SINGLE_UPLINK = 'test-uplink-timeout-single';
const PKG_MULTIPLE_UPLINKS = 'test-uplink-timeout-multiple';
export default function (server, server2, server3) {
describe('uplink connection timeouts', () => {
beforeAll(async () => {
await server2.addPackage(PKG_SINGLE_UPLINK).status(HTTP_STATUS.CREATED);
await server2.addPackage(PKG_MULTIPLE_UPLINKS).status(HTTP_STATUS.CREATED);
await server3.addPackage(PKG_MULTIPLE_UPLINKS).status(HTTP_STATUS.CREATED);
});
describe('get package', () => {
test('503 response when uplink connection ESOCKETTIMEDOUT', () => {
return server.getPackage(PKG_SINGLE_UPLINK).status(HTTP_STATUS.SERVICE_UNAVAILABLE);
});
test('200 response even though one uplink timesout', () => {
return server.getPackage(PKG_MULTIPLE_UPLINKS).status(HTTP_STATUS.OK)
});
});
});
}

@ -1,6 +1,15 @@
// @flow
import {HEADER_TYPE, HEADERS, HTTP_STATUS} from '../../../src/lib/constants';
import {HEADER_TYPE, HEADERS, HTTP_STATUS, TOKEN_BEARER} from '../../../src/lib/constants';
import {buildToken} from "../../../src/lib/utils";
// API Helpers
// This file should contain utilities to avoid repeated task over API unit testing,
// Please, comply with the following:
// - Promisify everything
// - Encourage using constants or create new ones if it's needed
// - // $FlowFixMe or any is fine if there is no other way
export function getPackage(
request: any,
@ -19,6 +28,24 @@ export function getPackage(
});
}
export function loginUserToken(request: any,
user: string,
credentials: any,
token: string,
statusCode: number = HTTP_STATUS.CREATED) {
// $FlowFixMe
return new Promise((resolve) => {
request.put(`/-/user/org.couchdb.user:${user}`)
.send(credentials)
.set('authorization', buildToken(TOKEN_BEARER, token))
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
.expect(statusCode)
.end(function(err, res) {
return resolve([err, res]);
});
});
}
export function addUser(request: any, user: string, credentials: any,
statusCode: number = HTTP_STATUS.CREATED) {
// $FlowFixMe
@ -50,7 +77,7 @@ export function getProfile(request: any, token: string, statusCode: number = HTT
// $FlowFixMe
return new Promise((resolve) => {
request.get(`/-/npm/v1/user`)
.set(HEADERS.AUTHORIZATION, `Bearer ${token}`)
.set('authorization', buildToken(TOKEN_BEARER, token))
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
.expect(statusCode)
.end(function(err, res) {

@ -7,12 +7,12 @@ import rimraf from 'rimraf';
import endPointAPI from '../../../../src/api';
import {HEADERS, HTTP_STATUS, HEADER_TYPE, TOKEN_BEARER, TOKEN_BASIC} from '../../../../src/lib/constants';
import {HEADERS, HTTP_STATUS, HEADER_TYPE, TOKEN_BEARER, TOKEN_BASIC, API_ERROR} from '../../../../src/lib/constants';
import {mockServer} from '../../__helper/mock';
import {DOMAIN_SERVERS} from '../../../functional/config.functional';
import {buildToken, parseConfigFile} from '../../../../src/lib/utils';
import {parseConfigurationFile} from '../../__helper';
import {addUser, getPackage} from '../../__helper/api';
import {addUser, getPackage, loginUserToken} from '../../__helper/api';
import {setup} from '../../../../src/lib/logger';
import {buildUserBuffer} from '../../../../src/lib/auth-utils';
@ -67,9 +67,11 @@ describe('endpoint user auth JWT unit test', () => {
expect(err).toBeNull();
expect(res.body.ok).toBeDefined();
expect(res.body.token).toBeDefined();
const token = res.body.token;
const { token } = res.body;
expect(typeof token).toBe('string');
expect(res.body.ok).toMatch(`user '${credentials.name}' created`);
// testing JWT auth headers with token
// we need it here, because token is required
const [err1, resp1] = await getPackage(request(app), buildToken(TOKEN_BEARER, token), 'vue');
@ -90,6 +92,7 @@ describe('endpoint user auth JWT unit test', () => {
await addUser(request(app), credentials.name, credentials);
// it should fails conflict 409
await addUser(request(app), credentials.name, credentials, HTTP_STATUS.CONFLICT);
// npm will try to sign in sending credentials via basic auth header
const token = buildUserBuffer(credentials.name, credentials.password).toString('base64');
request(app).put(`/-/user/org.couchdb.user:${credentials.name}/-rev/undefined`)
@ -101,6 +104,7 @@ describe('endpoint user auth JWT unit test', () => {
expect(err).toBeNull();
expect(res.body.ok).toBeDefined();
expect(res.body.token).toBeDefined();
done();
});
});
@ -113,4 +117,24 @@ describe('endpoint user auth JWT unit test', () => {
done();
});
test('should fails on login if user credentials are invalid even if jwt valid token is provided', async (done) => {
const credentials = { name: 'newFailsUser', password: 'secretPass' };
const [err, res] = await addUser(request(app), credentials.name, credentials);
expect(err).toBeNull();
expect(res.body.ok).toBeDefined();
expect(res.body.token).toBeDefined();
const { token } = res.body;
expect(typeof token).toBe('string');
expect(res.body.ok).toMatch(`user '${credentials.name}' created`);
// we login when token is valid
const newCredentials = { name: 'newFailsUser', password: 'BAD_PASSWORD' };
const [err2, resp2] = await loginUserToken(request(app), newCredentials.name, newCredentials, token, HTTP_STATUS.UNAUTHORIZED);
expect(err2).toBeNull();
expect(resp2.statusCode).toBe(HTTP_STATUS.UNAUTHORIZED);
expect(resp2.body.error).toMatch(API_ERROR.BAD_USERNAME_PASSWORD);
done();
});
});

@ -0,0 +1,94 @@
// @flow
/**
* PLEASE DO NOT MODIFY THIS FILE
*
* This test is just for teaching purpose, use this example as template for your new endpoint API unit test
*
* If you have any questions, ask at the http://chat.verdaccio.org #questions channel.
*
*/
import request from 'supertest';
import _ from 'lodash';
import path from 'path';
import rimraf from 'rimraf';
import endPointAPI from '../../../../src/api/index';
import {mockServer} from '../../__helper/mock';
import {DOMAIN_SERVERS} from '../../../functional/config.functional';
import {parseConfigFile} from '../../../../src/lib/utils';
import {parseConfigurationFile} from '../../__helper';
import {addUser} from '../../__helper/api';
import {setup} from '../../../../src/lib/logger';
// we must start logging without output
setup([]);
const parseConfigurationJWTFile = () => {
// Any new test must have a custom yaml file, try to name it based on the feature, the config
// file does not need to include all configuration, just the part is needs
// eg: test/unit/partials/config/yaml/api-jwt/jwt.yaml
return parseConfigurationFile(`api-jwt/jwt`);
};
describe('endpoint example unit test', () => {
let app;
let mockRegistry;
beforeAll(function(done) {
// 1. We create a route for a custom storage folder for this test
const store = path.join(__dirname, '../partials/store/test-jwt-storage');
// 2. The port must be unique (at this point this is not automated, need to be checked manually)
const mockServerPort = 55546;
// 3. Use rimraf to clean the state each time you run the test
rimraf(store, async () => {
// 4. Use a custom configuration file
const confS = parseConfigFile(parseConfigurationJWTFile());
// 5. Customise specific properties
const configForTest = _.assign({}, _.cloneDeep(confS), {
storage: store,
uplinks: {
npmjs: {
url: `http://${DOMAIN_SERVERS}:${mockServerPort}`
}
},
// 6. The self_path is important be the same as the store
self_path: store,
// 7. Define the location of the .htpasswd file, this is relative to self_path.
auth: {
htpasswd: {
file: './test-jwt-storage/.htpasswd'
}
}
});
// 8. Use the helper `endPointAPI` to mock the API
app = await endPointAPI(configForTest);
// 9 . Use `mockServer` to mock launch the server.
mockRegistry = await mockServer(mockServerPort).init();
done();
});
});
afterAll(function(done) {
// 10. Do not forget to stop the API, or it will run forever.
mockRegistry[0].stop();
done();
});
test('should test add a new user with JWT enabled', async (done) => {
// At this point the server is running and you can run the test
const credentials = { name: 'JotaJWT', password: 'secretPass' };
// 11. Use helpers for repetitive tasks
const [err, res] = await addUser(request(app), credentials.name, credentials);
// 12. test your output
expect(err).toBeNull();
expect(res.body.ok).toBeDefined();
expect(res.body.token).toBeDefined();
// 13. end the async test
done();
});
});

BIN
yarn.lock

Binary file not shown.