Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

allow everyone to modify users[username] in every package

  • Loading branch information...
commit 964f5470b399c3d5f1a1780211dc3c1fd57a6325 1 parent 310d9af
Jann Horn authored

Showing 1 changed file with 120 additions and 1 deletion. Show diff stats Hide diff stats

  1. +120 1 registry/app.js
121 registry/app.js
@@ -987,6 +987,68 @@ ddoc.updates.package = function (doc, req) {
987 987 return [d.getTime(), d.toUTCString(), ds, ts, tz]
988 988 }
989 989 }
  990 +
  991 + // extend o1 with o2 (in-place)
  992 + function deepExtend(o1, o2) {
  993 + for (var prop in o2)
  994 + if (o2.hasOwnProperty(prop))
  995 + if (o1.hasOwnProperty(prop)) {
  996 + if (typeof o1[prop] === "object")
  997 + deepExtend(o1[prop], o2[prop])
  998 + } else
  999 + o1[prop] = o2[prop]
  1000 + return o1
  1001 + }
  1002 +
  1003 + // Thank you for helping me with this code, kicken! my version looked so ugly...
  1004 + // Check whether two objects/arrays are equal while ignoring specific parts.
  1005 + // The root element must not be an ignored element.
  1006 + // Ignored parts may be added, altered or removed.
  1007 + // Usecase: Check whether someone only changed his "I use this" status of a
  1008 + // package.
  1009 + // ignoreKeys is an array of paths in the structure that musn't be checked.
  1010 + // Example:
  1011 + // var old = {name: "npm", version: "0.0.0", users: { isaacs: true, ryah: true }}
  1012 + // var new = {name: "npm", version: "0.0.0", users: { isaacs: true, ryah: true, thejh: true }}
  1013 + // var isAllowedChange = ignoringDeepEquals(old, new, ["users", user.name])
  1014 + function ignoringDeepEquals(o1, o2, ignoreKeys, pathPrefix){
  1015 + pathPrefix = pathPrefix || []
  1016 +
  1017 + function isObject(v){
  1018 + return typeof v === 'object'
  1019 + }
  1020 +
  1021 + // Check whether `arr` contains an array that's shallowly equal to `v`.
  1022 + function arrayInArray(v, arr) {
  1023 + return arr.some(function(e) {
  1024 + if (e.length !== v.length) return false
  1025 + for (var i=0; i<e.length; i++)
  1026 + if (e[i] !== v[i])
  1027 + return false
  1028 + return true
  1029 + })
  1030 + }
  1031 +
  1032 + function fullPath(p){
  1033 + return pathPrefix.concat([p])
  1034 + }
  1035 +
  1036 + if (typeof o1 !== typeof o2)
  1037 + return false
  1038 + else if (!isObject(o1))
  1039 + return o1 === o2
  1040 +
  1041 + for (var prop in o1)
  1042 + if (o1.hasOwnProperty(prop) && !arrayInArray(fullPath(prop), ignoreKeys))
  1043 + if (!o2.hasOwnProperty(prop) || !ignoringDeepEquals(o1[prop], o2[prop], ignoreKeys, fullPath(prop)))
  1044 + return false
  1045 +
  1046 + for (var prop in o2)
  1047 + if (o2.hasOwnProperty(prop) && !o1.hasOwnProperty(prop) && !arrayInArray(fullPath(prop), ignoreKeys))
  1048 + return false
  1049 +
  1050 + return true
  1051 + }
990 1052
991 1053 Object.keys = Object.keys
992 1054 || function (o) { var a = []
@@ -999,7 +1061,7 @@ ddoc.updates.package = function (doc, req) {
999 1061 return [{forbidden:reason}, JSON.stringify({forbidden:reason})]
1000 1062 }
1001 1063
1002   - function ok (doc, message) {
  1064 + function ok (doc, message, unmodified) {
1003 1065 delete doc.mtime
1004 1066 delete doc.ctime
1005 1067 var time = doc.time = doc.time || {}
@@ -1015,6 +1077,10 @@ ddoc.updates.package = function (doc, req) {
1015 1077 }
1016 1078
1017 1079 if (doc) {
  1080 + var extendedRequest = deepExtend(JSON.parse(req.body), doc)
  1081 + doc.users = doc.users || {}
  1082 + if (ignoringDeepEquals(extendedRequest, doc, [["users", req.userCtx.name]]))
  1083 + return [extendedRequest, JSON.stringify({ok:"updated 'use' status"})]
1018 1084 if (req.query.version) {
1019 1085 if (doc.url) {
1020 1086 return error(doc.name+" is hosted elsewhere: "+doc.url)
@@ -1058,6 +1124,10 @@ ddoc.updates.package = function (doc, req) {
1058 1124 if (body.description) doc.description = body.description
1059 1125 if (body.author) doc.author = body.author
1060 1126 if (body.repository) doc.repository = body.repository
  1127 + if (body.users && body.users[req.userCtx.name] != null) {
  1128 + if (!doc.users) doc.users = {}
  1129 + doc.users[req.userCtx.name] = body.users[req.userCtx.name]
  1130 + }
1061 1131 body.maintainers = doc.maintainers
1062 1132
1063 1133 var tag = req.query.tag
@@ -1162,6 +1232,49 @@ ddoc.validate_doc_update = function (newDoc, oldDoc, user) {
1162 1232 || (typeof a === "object" && typeof a.length === "number") }
1163 1233 var semver = require("semver")
1164 1234 var valid = require("valid")
  1235 +
  1236 + function ignoringDeepEquals(o1, o2, ignoreKeys, pathPrefix){
  1237 + pathPrefix = pathPrefix || []
  1238 +
  1239 + function isObject(v){
  1240 + return typeof v === 'object'
  1241 + }
  1242 +
  1243 + // Check whether `arr` contains an array that's shallowly equal to `v`.
  1244 + function arrayInArray(v, arr) {
  1245 + return arr.some(function(e) {
  1246 + if (e.length !== v.length) return false
  1247 + for (var i=0; i<e.length; i++)
  1248 + if (e[i] !== v[i])
  1249 + return false
  1250 + return true
  1251 + })
  1252 + }
  1253 +
  1254 + function fullPath(p){
  1255 + return pathPrefix.concat([p])
  1256 + }
  1257 +
  1258 + if (typeof o1 !== typeof o2)
  1259 + return false
  1260 + else if (!isObject(o1))
  1261 + return o1 === o2
  1262 +
  1263 + for (var prop in o1)
  1264 + if (o1.hasOwnProperty(prop) && !arrayInArray(fullPath(prop), ignoreKeys))
  1265 + if (!o2.hasOwnProperty(prop) || !ignoringDeepEquals(o1[prop], o2[prop], ignoreKeys, fullPath(prop)))
  1266 + return false
  1267 +
  1268 + for (var prop in o2)
  1269 + if (o2.hasOwnProperty(prop) && !o1.hasOwnProperty(prop) && !arrayInArray(fullPath(prop), ignoreKeys))
  1270 + return false
  1271 +
  1272 + return true
  1273 + }
  1274 +
  1275 + if (oldDoc) oldDoc.users = oldDoc.users || {}
  1276 + newDoc.users = newDoc.users || {}
  1277 +
1165 1278 // admins can do ANYTHING (even break stuff)
1166 1279 if (isAdmin()) return
1167 1280
@@ -1173,6 +1286,9 @@ ddoc.validate_doc_update = function (newDoc, oldDoc, user) {
1173 1286 // something detected in the _updates/package script.
1174 1287 assert(!newDoc.forbidden || newDoc._deleted, newDoc.forbidden)
1175 1288
  1289 + // everyone may alter his "I use this" status on any package
  1290 + if (oldDoc && !newDoc._deleted && ignoringDeepEquals(newDoc, oldDoc, [["users", user.name]])) return
  1291 +
1176 1292 function validUser () {
1177 1293 if ( !oldDoc || !oldDoc.maintainers ) return true
1178 1294 if (isAdmin()) return true
@@ -1235,4 +1351,7 @@ ddoc.validate_doc_update = function (newDoc, oldDoc, user) {
1235 1351 for (var i in newDoc.versions) {
1236 1352 assert(semver.valid(i), "version "+i+" is not a valid version")
1237 1353 }
  1354 +
  1355 + assert(ignoringDeepEquals(newDoc.users, (oldDoc || {users:{}}).users, [[user.name]]),
  1356 + "even the owner of a package may not fake 'I use this' data")
1238 1357 }

0 comments on commit 964f547

Please sign in to comment.
Something went wrong with that request. Please try again.