Skip to content

Commit 8fb8206

Browse files
authored
Perform haxelib validation on server side as well (#597)
* Perform haxelib validation on server side as well The client may be outdated or otherwise modified, so we cannot trust that it will always send valid libraries * Clean up error messages during server checks * Add tests for dependency checks in submit
1 parent 6bcfd6b commit 8fb8206

File tree

6 files changed

+96
-13
lines changed

6 files changed

+96
-13
lines changed

src/haxelib/SiteApi.hx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
package haxelib;
2323

2424
import haxelib.MetaData;
25+
import haxelib.Data.Dependencies;
2526
import haxe.ds.*;
2627

2728
interface SiteApi {
@@ -34,6 +35,7 @@ interface SiteApi {
3435
public function checkDeveloper( prj : String, user : String ) : Void;
3536
public function checkPassword( user : String, pass : String ) : Bool;
3637
public function getSubmitId() : String;
38+
public function checkDependencies( dependencies : Dependencies) : Void;
3739

3840
public function processSubmit( id : String, user : String, pass : String ) : String;
3941

src/haxelib/api/Connection.hx

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -323,7 +323,7 @@ class Connection {
323323
}
324324
#end
325325

326-
static function retry<R>(func:Void->R) {
326+
static function retry<R>(func:Void->R, errorPrefix = "Failed with error: ") {
327327
var hasRetried = false;
328328
var numTries = retries;
329329

@@ -339,7 +339,7 @@ class Connection {
339339
if (e == "std@host_resolve")
340340
Util.rethrow(e);
341341
if (e != "Blocked")
342-
throw 'Failed with error: $e';
342+
throw errorPrefix + e;
343343
log("Failed. Triggering retry due to HTTP timeout");
344344
hasRetried = true;
345345
}
@@ -418,16 +418,6 @@ class Connection {
418418
uploadAndSubmit(user, data, logUploadStatus);
419419
}
420420

421-
static function checkDependencies(dependencies:Dependencies) {
422-
for (name => versionString in dependencies) {
423-
final versions:Array<String> = getVersions(ProjectName.ofString(name));
424-
if (versionString == "")
425-
continue;
426-
if (!versions.contains(versionString))
427-
throw "Library " + name + " does not have version " + versionString;
428-
}
429-
}
430-
431421
static function doesVersionExist(library:ProjectName, version:SemVer):Bool {
432422
final versions = try getVersions(library) catch (_:Dynamic) return false;
433423
return versions.contains(version);
@@ -537,11 +527,14 @@ class Connection {
537527
return retry(data.site.checkPassword.bind(userName, password));
538528

539529
static function checkDeveloper(library:ProjectName, userName:String)
540-
return retry(data.site.checkDeveloper.bind(library, userName));
530+
return retry(data.site.checkDeveloper.bind(library, userName), "");
541531

542532
static function getSubmitId()
543533
return retry(data.site.getSubmitId.bind());
544534

545535
static function processSubmit(id:String, userName:String, password:String)
546536
return retry(data.site.processSubmit.bind(id, userName, password));
537+
538+
static function checkDependencies(dependencies:Dependencies)
539+
return retry(data.site.checkDependencies.bind(dependencies), "");
547540
}

src/haxelib/server/Repo.hx

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,27 @@ class Repo implements SiteApi {
154154
return Std.string(Std.random(100000000));
155155
}
156156

157+
public function checkDependencies(dependencies:Dependencies) : Void {
158+
for (dependency in dependencies.getNames()) {
159+
var project = Project.manager.select($name == dependency);
160+
if (project == null) {
161+
throw 'Library $dependency does not exist';
162+
}
163+
var version = Reflect.field(dependencies, dependency);
164+
if (version == '')
165+
continue;
166+
var hasVersion = false;
167+
for (versionObject in Version.manager.search($project == project.id)) {
168+
if (versionObject.name == version) {
169+
hasVersion = true;
170+
}
171+
}
172+
if (!hasVersion) {
173+
throw 'Library $dependency does not have version $version';
174+
}
175+
}
176+
}
177+
157178
public function processSubmit( id : String, user : String, pass : String ) : String {
158179
neko.Web.logMessage("processSubmit " + id);
159180
var tmpFile = Path.join([TMP_DIR_NAME, Std.parseInt(id)+".tmp"]);
@@ -170,6 +191,13 @@ class Repo implements SiteApi {
170191
var infos = Data.readDataFromZip(zip,CheckData);
171192
neko.Web.logMessage("processSubmit " + id + " readDataFromZip completed");
172193

194+
Data.checkClassPath(zip, infos);
195+
Data.checkDocumentation(zip, infos);
196+
197+
neko.Web.logMessage("processSubmit " + id + " metadata validation completed");
198+
199+
checkDependencies(infos.dependencies);
200+
173201
Manager.cnx.startTransaction();
174202

175203
var u = User.manager.search({ name : user }).first();
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"name": "Foo",
3+
"url" : "http://example.org",
4+
"license": "GPL",
5+
"tags": ["foo", "test"],
6+
"description": "This project depends on a library that doesn't exist on the server",
7+
"version": "1.0.0",
8+
"releasenote": "",
9+
"dependencies": {
10+
"MissingDep": ""
11+
},
12+
"contributors": ["Foo"]
13+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"name": "Foo",
3+
"url" : "http://example.org",
4+
"license": "GPL",
5+
"tags": ["foo", "test"],
6+
"description": "This project depends on a version of Bar that doesn't exist on the server",
7+
"version": "1.0.0",
8+
"releasenote": "",
9+
"dependencies": {
10+
"Bar": "2.0.0"
11+
},
12+
"contributors": ["Foo"]
13+
}

test/tests/integration/TestSubmit.hx

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,40 @@ class TestSubmit extends IntegrationTests {
4646
}
4747
}
4848

49+
function testLibraryWithMissingDep() {
50+
final r = haxelib(["register", foo.user, foo.email, foo.fullname, foo.pw, foo.pw]).result();
51+
assertSuccess(r);
52+
53+
final r = haxelib(["submit", Path.join([IntegrationTests.projectRoot, "test/libraries/libMissingDep.zip"]), foo.pw]).result();
54+
assertFail(r);
55+
assertEquals("Error: Library MissingDep does not exist", r.err.trim());
56+
57+
final r = haxelib(["search", "Foo"]).result();
58+
// did not get submitted
59+
assertFalse(r.out.indexOf("Foo") >= 0);
60+
}
61+
62+
function testLibraryWithMissingDepVersion() {
63+
final r = haxelib(["register", bar.user, bar.email, bar.fullname, bar.pw, bar.pw]).result();
64+
assertSuccess(r);
65+
66+
final r = haxelib(["submit", Path.join([IntegrationTests.projectRoot, "test/libraries/libBar.zip"]), bar.pw]).result();
67+
assertSuccess(r);
68+
69+
final r = haxelib(["register", foo.user, foo.email, foo.fullname, foo.pw, foo.pw]).result();
70+
assertSuccess(r);
71+
72+
final r = haxelib(["submit", Path.join([IntegrationTests.projectRoot, "test/libraries/libMissingDepVersion.zip"]), foo.pw]).result();
73+
assertFail(r);
74+
assertEquals("Error: Library Bar does not have version 2.0.0", r.err.trim());
75+
76+
trace("hello");
77+
78+
final r = haxelib(["search", "Foo"]).result();
79+
// did not get submitted
80+
assertFalse(r.out.indexOf("Foo") >= 0);
81+
}
82+
4983
function testLibraryWithGitDep() {
5084
// git deps should not be allowed in published versions
5185
// https://github.com/HaxeFoundation/haxelib/pull/344#issuecomment-244006799

0 commit comments

Comments
 (0)