@fedify/lint 2.4.0-dev.1599 → 2.4.0-dev.1634
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -0
- package/deno.json +1 -1
- package/dist/index.cjs +27 -23
- package/dist/index.js +9 -5
- package/dist/{outbox-listener-delivery-required-Cx3YNy0p.js → outbox-listener-delivery-required-BOIQaemq.js} +435 -24
- package/dist/{outbox-listener-delivery-required-BBuAbkOF.cjs → outbox-listener-delivery-required-BhuVwj9X.cjs} +458 -23
- package/dist/oxlint.cjs +27 -23
- package/dist/oxlint.js +9 -5
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -127,11 +127,19 @@ federation code:
|
|
|
127
127
|
when `setInboxListeners` is set
|
|
128
128
|
- **`actor-shared-inbox-property-mismatch`**: Validates sharedInbox URI from
|
|
129
129
|
`getInboxUri`
|
|
130
|
+
- **`actor-upload-media-property-required`**: Ensures endpoints.uploadMedia is
|
|
131
|
+
defined when `setMediaUploader` is set
|
|
132
|
+
- **`actor-upload-media-property-mismatch`**: Validates endpoints.uploadMedia
|
|
133
|
+
URI from `getMediaUploaderUri`
|
|
130
134
|
|
|
131
135
|
### Other rules
|
|
132
136
|
|
|
133
137
|
- **`collection-filtering-not-implemented`**: Warns about missing collection
|
|
134
138
|
filtering implementation (`setFollowersDispatcher` only for now)
|
|
139
|
+
- **`media-uploader-object-uri-required`**: Warns when a `setMediaUploader`
|
|
140
|
+
callback does not derive its return value from `getObjectUri`
|
|
141
|
+
- **`media-uploader-authorization-required`**: Warns when `setMediaUploader`
|
|
142
|
+
is registered without an `authorize` hook
|
|
135
143
|
|
|
136
144
|
|
|
137
145
|
Installation
|
package/deno.json
CHANGED
package/dist/index.cjs
CHANGED
|
@@ -24,7 +24,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
24
24
|
enumerable: true
|
|
25
25
|
}) : target, mod));
|
|
26
26
|
//#endregion
|
|
27
|
-
const require_outbox_listener_delivery_required = require("./outbox-listener-delivery-required-
|
|
27
|
+
const require_outbox_listener_delivery_required = require("./outbox-listener-delivery-required-BhuVwj9X.cjs");
|
|
28
28
|
let _fxts_core = require("@fxts/core");
|
|
29
29
|
let _typescript_eslint_parser = require("@typescript-eslint/parser");
|
|
30
30
|
_typescript_eslint_parser = __toESM(_typescript_eslint_parser, 1);
|
|
@@ -34,28 +34,32 @@ _typescript_eslint_parser = __toESM(_typescript_eslint_parser, 1);
|
|
|
34
34
|
* Provides lint rules for validating Fedify federation code.
|
|
35
35
|
*/
|
|
36
36
|
const rules = {
|
|
37
|
-
[require_outbox_listener_delivery_required.RULE_IDS.actorIdMismatch]: require_outbox_listener_delivery_required.eslint$
|
|
38
|
-
[require_outbox_listener_delivery_required.RULE_IDS.actorIdRequired]: require_outbox_listener_delivery_required.eslint$
|
|
39
|
-
[require_outbox_listener_delivery_required.RULE_IDS.actorFollowingPropertyRequired]: require_outbox_listener_delivery_required.eslint$
|
|
40
|
-
[require_outbox_listener_delivery_required.RULE_IDS.actorFollowingPropertyMismatch]: require_outbox_listener_delivery_required.eslint$
|
|
41
|
-
[require_outbox_listener_delivery_required.RULE_IDS.actorFollowersPropertyRequired]: require_outbox_listener_delivery_required.eslint$
|
|
42
|
-
[require_outbox_listener_delivery_required.RULE_IDS.actorFollowersPropertyMismatch]: require_outbox_listener_delivery_required.eslint$
|
|
43
|
-
[require_outbox_listener_delivery_required.RULE_IDS.actorOutboxPropertyRequired]: require_outbox_listener_delivery_required.eslint$
|
|
44
|
-
[require_outbox_listener_delivery_required.RULE_IDS.actorOutboxPropertyMismatch]: require_outbox_listener_delivery_required.eslint$
|
|
45
|
-
[require_outbox_listener_delivery_required.RULE_IDS.actorLikedPropertyRequired]: require_outbox_listener_delivery_required.eslint$
|
|
46
|
-
[require_outbox_listener_delivery_required.RULE_IDS.actorLikedPropertyMismatch]: require_outbox_listener_delivery_required.eslint$
|
|
47
|
-
[require_outbox_listener_delivery_required.RULE_IDS.actorFeaturedPropertyRequired]: require_outbox_listener_delivery_required.eslint$
|
|
48
|
-
[require_outbox_listener_delivery_required.RULE_IDS.actorFeaturedPropertyMismatch]: require_outbox_listener_delivery_required.eslint$
|
|
49
|
-
[require_outbox_listener_delivery_required.RULE_IDS.actorFeaturedTagsPropertyRequired]: require_outbox_listener_delivery_required.eslint$
|
|
50
|
-
[require_outbox_listener_delivery_required.RULE_IDS.actorFeaturedTagsPropertyMismatch]: require_outbox_listener_delivery_required.eslint$
|
|
51
|
-
[require_outbox_listener_delivery_required.RULE_IDS.actorInboxPropertyRequired]: require_outbox_listener_delivery_required.eslint$
|
|
52
|
-
[require_outbox_listener_delivery_required.RULE_IDS.actorInboxPropertyMismatch]: require_outbox_listener_delivery_required.eslint$
|
|
53
|
-
[require_outbox_listener_delivery_required.RULE_IDS.actorSharedInboxPropertyRequired]: require_outbox_listener_delivery_required.eslint$
|
|
54
|
-
[require_outbox_listener_delivery_required.RULE_IDS.actorSharedInboxPropertyMismatch]: require_outbox_listener_delivery_required.eslint$
|
|
55
|
-
[require_outbox_listener_delivery_required.RULE_IDS.
|
|
56
|
-
[require_outbox_listener_delivery_required.RULE_IDS.
|
|
57
|
-
[require_outbox_listener_delivery_required.RULE_IDS.
|
|
58
|
-
[require_outbox_listener_delivery_required.RULE_IDS.
|
|
37
|
+
[require_outbox_listener_delivery_required.RULE_IDS.actorIdMismatch]: require_outbox_listener_delivery_required.eslint$16,
|
|
38
|
+
[require_outbox_listener_delivery_required.RULE_IDS.actorIdRequired]: require_outbox_listener_delivery_required.eslint$15,
|
|
39
|
+
[require_outbox_listener_delivery_required.RULE_IDS.actorFollowingPropertyRequired]: require_outbox_listener_delivery_required.eslint$17,
|
|
40
|
+
[require_outbox_listener_delivery_required.RULE_IDS.actorFollowingPropertyMismatch]: require_outbox_listener_delivery_required.eslint$18,
|
|
41
|
+
[require_outbox_listener_delivery_required.RULE_IDS.actorFollowersPropertyRequired]: require_outbox_listener_delivery_required.eslint$19,
|
|
42
|
+
[require_outbox_listener_delivery_required.RULE_IDS.actorFollowersPropertyMismatch]: require_outbox_listener_delivery_required.eslint$20,
|
|
43
|
+
[require_outbox_listener_delivery_required.RULE_IDS.actorOutboxPropertyRequired]: require_outbox_listener_delivery_required.eslint$9,
|
|
44
|
+
[require_outbox_listener_delivery_required.RULE_IDS.actorOutboxPropertyMismatch]: require_outbox_listener_delivery_required.eslint$10,
|
|
45
|
+
[require_outbox_listener_delivery_required.RULE_IDS.actorLikedPropertyRequired]: require_outbox_listener_delivery_required.eslint$11,
|
|
46
|
+
[require_outbox_listener_delivery_required.RULE_IDS.actorLikedPropertyMismatch]: require_outbox_listener_delivery_required.eslint$12,
|
|
47
|
+
[require_outbox_listener_delivery_required.RULE_IDS.actorFeaturedPropertyRequired]: require_outbox_listener_delivery_required.eslint$23,
|
|
48
|
+
[require_outbox_listener_delivery_required.RULE_IDS.actorFeaturedPropertyMismatch]: require_outbox_listener_delivery_required.eslint$24,
|
|
49
|
+
[require_outbox_listener_delivery_required.RULE_IDS.actorFeaturedTagsPropertyRequired]: require_outbox_listener_delivery_required.eslint$21,
|
|
50
|
+
[require_outbox_listener_delivery_required.RULE_IDS.actorFeaturedTagsPropertyMismatch]: require_outbox_listener_delivery_required.eslint$22,
|
|
51
|
+
[require_outbox_listener_delivery_required.RULE_IDS.actorInboxPropertyRequired]: require_outbox_listener_delivery_required.eslint$13,
|
|
52
|
+
[require_outbox_listener_delivery_required.RULE_IDS.actorInboxPropertyMismatch]: require_outbox_listener_delivery_required.eslint$14,
|
|
53
|
+
[require_outbox_listener_delivery_required.RULE_IDS.actorSharedInboxPropertyRequired]: require_outbox_listener_delivery_required.eslint$6,
|
|
54
|
+
[require_outbox_listener_delivery_required.RULE_IDS.actorSharedInboxPropertyMismatch]: require_outbox_listener_delivery_required.eslint$7,
|
|
55
|
+
[require_outbox_listener_delivery_required.RULE_IDS.actorUploadMediaPropertyRequired]: require_outbox_listener_delivery_required.eslint$4,
|
|
56
|
+
[require_outbox_listener_delivery_required.RULE_IDS.actorUploadMediaPropertyMismatch]: require_outbox_listener_delivery_required.eslint$5,
|
|
57
|
+
[require_outbox_listener_delivery_required.RULE_IDS.actorPublicKeyRequired]: require_outbox_listener_delivery_required.eslint$8,
|
|
58
|
+
[require_outbox_listener_delivery_required.RULE_IDS.actorAssertionMethodRequired]: require_outbox_listener_delivery_required.eslint$25,
|
|
59
|
+
[require_outbox_listener_delivery_required.RULE_IDS.collectionFilteringNotImplemented]: require_outbox_listener_delivery_required.eslint$3,
|
|
60
|
+
[require_outbox_listener_delivery_required.RULE_IDS.outboxListenerDeliveryRequired]: require_outbox_listener_delivery_required.eslint,
|
|
61
|
+
[require_outbox_listener_delivery_required.RULE_IDS.mediaUploaderObjectUriRequired]: require_outbox_listener_delivery_required.eslint$1,
|
|
62
|
+
[require_outbox_listener_delivery_required.RULE_IDS.mediaUploaderAuthorizationRequired]: require_outbox_listener_delivery_required.eslint$2
|
|
59
63
|
};
|
|
60
64
|
const recommendedRuleIds = [require_outbox_listener_delivery_required.RULE_IDS.actorIdMismatch, require_outbox_listener_delivery_required.RULE_IDS.actorIdRequired];
|
|
61
65
|
/**
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { C as RULE_IDS, S as eslint$
|
|
1
|
+
import { C as eslint$13, D as RULE_IDS, E as eslint$21, O as name, S as eslint$12, T as eslint$11, _ as eslint, a as eslint$18, b as eslint$4, c as eslint$17, d as eslint$7, f as eslint$8, g as eslint$1, h as eslint$15, i as eslint$22, k as version, l as eslint$20, m as eslint$14, n as eslint$24, o as eslint$19, p as eslint$9, r as eslint$25, s as eslint$16, t as eslint$23, u as eslint$6, v as eslint$2, w as eslint$10, x as eslint$5, y as eslint$3 } from "./outbox-listener-delivery-required-BOIQaemq.js";
|
|
2
2
|
import { fromEntries, keys, map, pipe } from "@fxts/core";
|
|
3
3
|
import parser from "@typescript-eslint/parser";
|
|
4
4
|
//#region src/index.ts
|
|
@@ -25,10 +25,14 @@ const rules = {
|
|
|
25
25
|
[RULE_IDS.actorInboxPropertyMismatch]: eslint$15,
|
|
26
26
|
[RULE_IDS.actorSharedInboxPropertyRequired]: eslint$16,
|
|
27
27
|
[RULE_IDS.actorSharedInboxPropertyMismatch]: eslint$17,
|
|
28
|
-
[RULE_IDS.
|
|
29
|
-
[RULE_IDS.
|
|
30
|
-
[RULE_IDS.
|
|
31
|
-
[RULE_IDS.
|
|
28
|
+
[RULE_IDS.actorUploadMediaPropertyRequired]: eslint$18,
|
|
29
|
+
[RULE_IDS.actorUploadMediaPropertyMismatch]: eslint$19,
|
|
30
|
+
[RULE_IDS.actorPublicKeyRequired]: eslint$20,
|
|
31
|
+
[RULE_IDS.actorAssertionMethodRequired]: eslint$21,
|
|
32
|
+
[RULE_IDS.collectionFilteringNotImplemented]: eslint$22,
|
|
33
|
+
[RULE_IDS.outboxListenerDeliveryRequired]: eslint$23,
|
|
34
|
+
[RULE_IDS.mediaUploaderObjectUriRequired]: eslint$24,
|
|
35
|
+
[RULE_IDS.mediaUploaderAuthorizationRequired]: eslint$25
|
|
32
36
|
};
|
|
33
37
|
const recommendedRuleIds = [RULE_IDS.actorIdMismatch, RULE_IDS.actorIdRequired];
|
|
34
38
|
/**
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { always, every, head, isEmpty, isObject, negate, pipe, pipeLazy, prop, some, toArray, unless, when } from "@fxts/core";
|
|
2
2
|
//#region deno.json
|
|
3
3
|
var name = "@fedify/lint";
|
|
4
|
-
var version = "2.4.0-dev.
|
|
4
|
+
var version = "2.4.0-dev.1634+3b4bfbb6";
|
|
5
5
|
//#endregion
|
|
6
6
|
//#region src/lib/const.ts
|
|
7
7
|
/**
|
|
@@ -77,6 +77,17 @@ const properties = {
|
|
|
77
77
|
wrapper: "Endpoints"
|
|
78
78
|
}
|
|
79
79
|
},
|
|
80
|
+
uploadMedia: {
|
|
81
|
+
name: "uploadMedia",
|
|
82
|
+
path: ["endpoints", "uploadMedia"],
|
|
83
|
+
getter: "getMediaUploaderUri",
|
|
84
|
+
setter: "setMediaUploader",
|
|
85
|
+
requiresIdentifier: true,
|
|
86
|
+
nested: {
|
|
87
|
+
parent: "endpoints",
|
|
88
|
+
wrapper: "Endpoints"
|
|
89
|
+
}
|
|
90
|
+
},
|
|
80
91
|
publicKey: {
|
|
81
92
|
name: "publicKey",
|
|
82
93
|
path: ["publicKey"],
|
|
@@ -107,6 +118,7 @@ const RULE_IDS = {
|
|
|
107
118
|
actorFeaturedTagsPropertyRequired: "actor-featured-tags-property-required",
|
|
108
119
|
actorInboxPropertyRequired: "actor-inbox-property-required",
|
|
109
120
|
actorSharedInboxPropertyRequired: "actor-shared-inbox-property-required",
|
|
121
|
+
actorUploadMediaPropertyRequired: "actor-upload-media-property-required",
|
|
110
122
|
actorPublicKeyRequired: "actor-public-key-required",
|
|
111
123
|
actorAssertionMethodRequired: "actor-assertion-method-required",
|
|
112
124
|
actorIdMismatch: "actor-id-mismatch",
|
|
@@ -118,8 +130,11 @@ const RULE_IDS = {
|
|
|
118
130
|
actorFeaturedTagsPropertyMismatch: "actor-featured-tags-property-mismatch",
|
|
119
131
|
actorInboxPropertyMismatch: "actor-inbox-property-mismatch",
|
|
120
132
|
actorSharedInboxPropertyMismatch: "actor-shared-inbox-property-mismatch",
|
|
133
|
+
actorUploadMediaPropertyMismatch: "actor-upload-media-property-mismatch",
|
|
121
134
|
collectionFilteringNotImplemented: "collection-filtering-not-implemented",
|
|
122
|
-
outboxListenerDeliveryRequired: "outbox-listener-delivery-required"
|
|
135
|
+
outboxListenerDeliveryRequired: "outbox-listener-delivery-required",
|
|
136
|
+
mediaUploaderObjectUriRequired: "media-uploader-object-uri-required",
|
|
137
|
+
mediaUploaderAuthorizationRequired: "media-uploader-authorization-required"
|
|
123
138
|
};
|
|
124
139
|
//#endregion
|
|
125
140
|
//#region src/lib/messages.ts
|
|
@@ -417,7 +432,7 @@ function createRequiredRuleEslint(config) {
|
|
|
417
432
|
};
|
|
418
433
|
}
|
|
419
434
|
createRequiredRuleDeno(properties.assertionMethod);
|
|
420
|
-
const eslint$
|
|
435
|
+
const eslint$25 = createRequiredRuleEslint(properties.assertionMethod);
|
|
421
436
|
//#endregion
|
|
422
437
|
//#region src/lib/mismatch.ts
|
|
423
438
|
const isIdentifierWithName = (name) => (node) => allOf(isNodeType("Identifier"), isNodeName(name))(node);
|
|
@@ -487,43 +502,47 @@ const createMismatchRuleEslint = (config) => ({
|
|
|
487
502
|
}))
|
|
488
503
|
});
|
|
489
504
|
createMismatchRuleDeno(properties.featured);
|
|
490
|
-
const eslint$
|
|
505
|
+
const eslint$24 = createMismatchRuleEslint(properties.featured);
|
|
491
506
|
createRequiredRuleDeno(properties.featured);
|
|
492
|
-
const eslint$
|
|
507
|
+
const eslint$23 = createRequiredRuleEslint(properties.featured);
|
|
493
508
|
createMismatchRuleDeno(properties.featuredTags);
|
|
494
|
-
const eslint$
|
|
509
|
+
const eslint$22 = createMismatchRuleEslint(properties.featuredTags);
|
|
495
510
|
createRequiredRuleDeno(properties.featuredTags);
|
|
496
|
-
const eslint$
|
|
511
|
+
const eslint$21 = createRequiredRuleEslint(properties.featuredTags);
|
|
497
512
|
createMismatchRuleDeno(properties.followers);
|
|
498
|
-
const eslint$
|
|
513
|
+
const eslint$20 = createMismatchRuleEslint(properties.followers);
|
|
499
514
|
createRequiredRuleDeno(properties.followers);
|
|
500
|
-
const eslint$
|
|
515
|
+
const eslint$19 = createRequiredRuleEslint(properties.followers);
|
|
501
516
|
createMismatchRuleDeno(properties.following);
|
|
502
|
-
const eslint$
|
|
517
|
+
const eslint$18 = createMismatchRuleEslint(properties.following);
|
|
503
518
|
createRequiredRuleDeno(properties.following);
|
|
504
|
-
const eslint$
|
|
519
|
+
const eslint$17 = createRequiredRuleEslint(properties.following);
|
|
505
520
|
createMismatchRuleDeno(properties.id);
|
|
506
|
-
const eslint$
|
|
521
|
+
const eslint$16 = createMismatchRuleEslint(properties.id);
|
|
507
522
|
createRequiredRuleDeno(properties.id);
|
|
508
|
-
const eslint$
|
|
523
|
+
const eslint$15 = createRequiredRuleEslint(properties.id);
|
|
509
524
|
createMismatchRuleDeno(properties.inbox);
|
|
510
|
-
const eslint$
|
|
525
|
+
const eslint$14 = createMismatchRuleEslint(properties.inbox);
|
|
511
526
|
createRequiredRuleDeno(properties.inbox);
|
|
512
|
-
const eslint$
|
|
527
|
+
const eslint$13 = createRequiredRuleEslint(properties.inbox);
|
|
513
528
|
createMismatchRuleDeno(properties.liked);
|
|
514
|
-
const eslint$
|
|
529
|
+
const eslint$12 = createMismatchRuleEslint(properties.liked);
|
|
515
530
|
createRequiredRuleDeno(properties.liked);
|
|
516
|
-
const eslint$
|
|
531
|
+
const eslint$11 = createRequiredRuleEslint(properties.liked);
|
|
517
532
|
createMismatchRuleDeno(properties.outbox);
|
|
518
|
-
const eslint$
|
|
533
|
+
const eslint$10 = createMismatchRuleEslint(properties.outbox);
|
|
519
534
|
createRequiredRuleDeno(properties.outbox);
|
|
520
|
-
const eslint$
|
|
535
|
+
const eslint$9 = createRequiredRuleEslint(properties.outbox);
|
|
521
536
|
createRequiredRuleDeno(properties.publicKey);
|
|
522
|
-
const eslint$
|
|
537
|
+
const eslint$8 = createRequiredRuleEslint(properties.publicKey);
|
|
523
538
|
createMismatchRuleDeno(properties.sharedInbox);
|
|
524
|
-
const eslint$
|
|
539
|
+
const eslint$7 = createMismatchRuleEslint(properties.sharedInbox);
|
|
525
540
|
createRequiredRuleDeno(properties.sharedInbox);
|
|
526
|
-
const eslint$
|
|
541
|
+
const eslint$6 = createRequiredRuleEslint(properties.sharedInbox);
|
|
542
|
+
createMismatchRuleDeno(properties.uploadMedia);
|
|
543
|
+
const eslint$5 = createMismatchRuleEslint(properties.uploadMedia);
|
|
544
|
+
createRequiredRuleDeno(properties.uploadMedia);
|
|
545
|
+
const eslint$4 = createRequiredRuleEslint(properties.uploadMedia);
|
|
527
546
|
//#endregion
|
|
528
547
|
//#region src/rules/collection-filtering-not-implemented.ts
|
|
529
548
|
/**
|
|
@@ -542,7 +561,7 @@ const isFollowersDispatcherCall = (node) => "callee" in node && node.callee && n
|
|
|
542
561
|
* CollectionDispatcher signature: (context, identifier, cursor, filter?) => ...
|
|
543
562
|
*/
|
|
544
563
|
const hasFilterParameter = hasMinParams(4);
|
|
545
|
-
const eslint$
|
|
564
|
+
const eslint$3 = {
|
|
546
565
|
meta: {
|
|
547
566
|
type: "suggestion",
|
|
548
567
|
docs: { description: "Ensure followers dispatcher implements filtering" },
|
|
@@ -567,6 +586,398 @@ const eslint$1 = {
|
|
|
567
586
|
}
|
|
568
587
|
};
|
|
569
588
|
//#endregion
|
|
589
|
+
//#region src/rules/media-uploader-authorization-required.ts
|
|
590
|
+
const MESSAGE$2 = "setMediaUploader() should be protected with .authorize(); without it the endpoint accepts uploads from anyone who can reach the URL. Add .authorize() unless a public upload endpoint is intended.";
|
|
591
|
+
function getParent(node) {
|
|
592
|
+
if (node == null || typeof node !== "object") return null;
|
|
593
|
+
const parent = node.parent;
|
|
594
|
+
return isNode(parent) ? parent : null;
|
|
595
|
+
}
|
|
596
|
+
/**
|
|
597
|
+
* Whether the given `setMediaUploader(...)` call is directly chained with
|
|
598
|
+
* `.authorize(...)`, i.e. `federation.setMediaUploader(...).authorize(...)`.
|
|
599
|
+
*/
|
|
600
|
+
function isDirectlyAuthorized(callNode) {
|
|
601
|
+
const member = getParent(callNode);
|
|
602
|
+
if (member == null || member.type !== "MemberExpression" || member.computed === true) return false;
|
|
603
|
+
const property = member.property;
|
|
604
|
+
if (!isNode(property) || property.type !== "Identifier" || property.name !== "authorize") return false;
|
|
605
|
+
const call = getParent(member);
|
|
606
|
+
return call != null && call.type === "CallExpression" && call.callee === member;
|
|
607
|
+
}
|
|
608
|
+
function createRule$2(buildReport) {
|
|
609
|
+
return (context) => {
|
|
610
|
+
const federationTracker = trackFederationVariables();
|
|
611
|
+
const unchained = [];
|
|
612
|
+
const authorizedVars = /* @__PURE__ */ new Set();
|
|
613
|
+
return {
|
|
614
|
+
VariableDeclarator: federationTracker.VariableDeclarator,
|
|
615
|
+
CallExpression(node) {
|
|
616
|
+
if (hasMemberExpressionCallee(node) && hasIdentifierProperty(node) && hasMethodName("authorize")(node)) {
|
|
617
|
+
const object = node.callee.object;
|
|
618
|
+
if (isNode(object) && object.type === "Identifier") authorizedVars.add(object.name);
|
|
619
|
+
return;
|
|
620
|
+
}
|
|
621
|
+
if (!hasMemberExpressionCallee(node) || !hasIdentifierProperty(node) || !hasMethodName("setMediaUploader")(node) || !federationTracker.isFederationObject(node.callee.object)) return;
|
|
622
|
+
if (isDirectlyAuthorized(node)) return;
|
|
623
|
+
const parent = getParent(node);
|
|
624
|
+
let varName = null;
|
|
625
|
+
if (parent != null && parent.type === "VariableDeclarator") {
|
|
626
|
+
const id = parent.id;
|
|
627
|
+
if (isNode(id) && id.type === "Identifier") varName = id.name;
|
|
628
|
+
}
|
|
629
|
+
unchained.push({
|
|
630
|
+
node,
|
|
631
|
+
varName
|
|
632
|
+
});
|
|
633
|
+
},
|
|
634
|
+
"Program:exit"() {
|
|
635
|
+
for (const { node, varName } of unchained) {
|
|
636
|
+
if (varName != null && authorizedVars.has(varName)) continue;
|
|
637
|
+
context.report({
|
|
638
|
+
node,
|
|
639
|
+
...buildReport
|
|
640
|
+
});
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
};
|
|
644
|
+
};
|
|
645
|
+
}
|
|
646
|
+
const eslint$2 = {
|
|
647
|
+
meta: {
|
|
648
|
+
type: "suggestion",
|
|
649
|
+
docs: { description: "Warn when setMediaUploader() is registered without an authorize() hook" },
|
|
650
|
+
schema: [],
|
|
651
|
+
messages: { required: "{{ message }}" }
|
|
652
|
+
},
|
|
653
|
+
create: createRule$2({
|
|
654
|
+
messageId: "required",
|
|
655
|
+
data: { message: MESSAGE$2 }
|
|
656
|
+
})
|
|
657
|
+
};
|
|
658
|
+
//#endregion
|
|
659
|
+
//#region src/rules/media-uploader-object-uri-required.ts
|
|
660
|
+
const MESSAGE$1 = "setMediaUploader() callbacks should return a value derived from ctx.getObjectUri(): either return ctx.getObjectUri(...) directly, or an object whose id is ctx.getObjectUri(...).";
|
|
661
|
+
const GETTER_NAME = "getObjectUri";
|
|
662
|
+
const getMemberPropertyName$1 = (node) => {
|
|
663
|
+
if (node.type !== "MemberExpression") return null;
|
|
664
|
+
const property = node.property;
|
|
665
|
+
if (property.type === "Identifier") return property.name;
|
|
666
|
+
if (property.type === "Literal" && typeof property.value === "string") return property.value;
|
|
667
|
+
return null;
|
|
668
|
+
};
|
|
669
|
+
function unwrapAssignmentPattern(node) {
|
|
670
|
+
let current = node ?? null;
|
|
671
|
+
while (current?.type === "AssignmentPattern") current = current.left;
|
|
672
|
+
return current;
|
|
673
|
+
}
|
|
674
|
+
/**
|
|
675
|
+
* Resolves the callback argument to a function node, following simple bindings
|
|
676
|
+
* when the callback is passed by reference (identifier or object member).
|
|
677
|
+
*/
|
|
678
|
+
const resolveCallbackReference = (expr, bindings, seen = /* @__PURE__ */ new Set()) => {
|
|
679
|
+
if (isFunction(expr)) return expr;
|
|
680
|
+
if (expr.type === "Identifier") {
|
|
681
|
+
if (seen.has(expr.name)) return null;
|
|
682
|
+
seen.add(expr.name);
|
|
683
|
+
const binding = bindings.get(expr.name);
|
|
684
|
+
if (binding == null || !isNode(binding)) return null;
|
|
685
|
+
if (isFunction(binding) || binding.type === "FunctionDeclaration") return binding;
|
|
686
|
+
if (binding.type === "Identifier") return resolveCallbackReference(binding, bindings, seen);
|
|
687
|
+
return null;
|
|
688
|
+
}
|
|
689
|
+
if (expr.type === "MemberExpression" && expr.object.type === "Identifier" && !expr.computed) {
|
|
690
|
+
const binding = bindings.get(expr.object.name);
|
|
691
|
+
if (binding == null || !isNode(binding) || binding.type !== "ObjectExpression") return null;
|
|
692
|
+
const propertyName = getMemberPropertyName$1(expr);
|
|
693
|
+
if (propertyName == null) return null;
|
|
694
|
+
for (const prop of binding.properties) {
|
|
695
|
+
if (!isNode(prop) || prop.type !== "Property") continue;
|
|
696
|
+
if ((prop.key.type === "Identifier" ? prop.key.name : prop.key.type === "Literal" && typeof prop.key.value === "string" ? prop.key.value : null) !== propertyName || !isNode(prop.value)) continue;
|
|
697
|
+
const value = prop.value;
|
|
698
|
+
if (isFunction(value) || value.type === "FunctionDeclaration") return value;
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
return null;
|
|
702
|
+
};
|
|
703
|
+
/**
|
|
704
|
+
* Returns the identifier name of the callback's context parameter, used to
|
|
705
|
+
* require that `getObjectUri()` is called as a member of the context itself
|
|
706
|
+
* (`ctx.getObjectUri(...)`), not on some other object. Returns `null` when the
|
|
707
|
+
* parameter is destructured: destructuring `getObjectUri` off the context
|
|
708
|
+
* (`({ getObjectUri }) => getObjectUri(...)`) loses the method's receiver and
|
|
709
|
+
* throws at runtime, so that form is intentionally not recognized as valid.
|
|
710
|
+
*/
|
|
711
|
+
function getContextParamName(callback) {
|
|
712
|
+
const contextParam = unwrapAssignmentPattern(callback.params[0]);
|
|
713
|
+
return contextParam?.type === "Identifier" ? contextParam.name : null;
|
|
714
|
+
}
|
|
715
|
+
const FUNCTION_NODE_TYPES = new Set([
|
|
716
|
+
"FunctionDeclaration",
|
|
717
|
+
"FunctionExpression",
|
|
718
|
+
"ArrowFunctionExpression"
|
|
719
|
+
]);
|
|
720
|
+
/**
|
|
721
|
+
* Collects `const`/`let`/`var name = init` bindings declared inside the
|
|
722
|
+
* callback body (excluding nested functions), so an identifier used in a
|
|
723
|
+
* returned expression can be resolved back to the expression it was assigned.
|
|
724
|
+
*/
|
|
725
|
+
function collectLocalBindings(node, bindings) {
|
|
726
|
+
if (node == null || typeof node !== "object") return;
|
|
727
|
+
if (Array.isArray(node)) {
|
|
728
|
+
for (const child of node) collectLocalBindings(child, bindings);
|
|
729
|
+
return;
|
|
730
|
+
}
|
|
731
|
+
const n = node;
|
|
732
|
+
if (typeof n.type !== "string") return;
|
|
733
|
+
if (FUNCTION_NODE_TYPES.has(n.type)) return;
|
|
734
|
+
if (n.type === "VariableDeclarator") {
|
|
735
|
+
const id = n.id;
|
|
736
|
+
if (id != null && isNode(id) && id.type === "Identifier" && n.init != null) {
|
|
737
|
+
if (!bindings.has(id.name)) bindings.set(id.name, n.init);
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
for (const key in n) {
|
|
741
|
+
if (key === "parent") continue;
|
|
742
|
+
collectLocalBindings(n[key], bindings);
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
/**
|
|
746
|
+
* Collects the returned expressions of the callback: the argument of every
|
|
747
|
+
* `return` statement (excluding nested functions), or, for an
|
|
748
|
+
* expression-bodied arrow, the body expression itself. A bare `return;`
|
|
749
|
+
* contributes `null`.
|
|
750
|
+
*/
|
|
751
|
+
function collectReturnedExpressions(callback) {
|
|
752
|
+
const body = callback.body;
|
|
753
|
+
if (body != null && isNode(body) && body.type !== "BlockStatement") return [body];
|
|
754
|
+
const result = [];
|
|
755
|
+
const walk = (node) => {
|
|
756
|
+
if (node == null || typeof node !== "object") return;
|
|
757
|
+
if (Array.isArray(node)) {
|
|
758
|
+
for (const child of node) walk(child);
|
|
759
|
+
return;
|
|
760
|
+
}
|
|
761
|
+
const n = node;
|
|
762
|
+
if (typeof n.type !== "string") return;
|
|
763
|
+
if (n.type === "ReturnStatement") {
|
|
764
|
+
result.push(n.argument ?? null);
|
|
765
|
+
return;
|
|
766
|
+
}
|
|
767
|
+
if (FUNCTION_NODE_TYPES.has(n.type)) return;
|
|
768
|
+
for (const key in n) {
|
|
769
|
+
if (key === "parent") continue;
|
|
770
|
+
walk(n[key]);
|
|
771
|
+
}
|
|
772
|
+
};
|
|
773
|
+
walk(body);
|
|
774
|
+
return result;
|
|
775
|
+
}
|
|
776
|
+
function toNode(value) {
|
|
777
|
+
if (value == null || typeof value !== "object" || Array.isArray(value)) return null;
|
|
778
|
+
const n = value;
|
|
779
|
+
return typeof n.type === "string" ? n : null;
|
|
780
|
+
}
|
|
781
|
+
/** Whether the node is a `Promise.resolve(x)` call. */
|
|
782
|
+
function isPromiseResolveCall(expr) {
|
|
783
|
+
if (expr.type !== "CallExpression") return false;
|
|
784
|
+
const callee = toNode(expr.callee);
|
|
785
|
+
if (callee == null || callee.type !== "MemberExpression" || getMemberPropertyName$1(callee) !== "resolve") return false;
|
|
786
|
+
const object = toNode(callee.object);
|
|
787
|
+
return object != null && object.type === "Identifier" && object.name === "Promise";
|
|
788
|
+
}
|
|
789
|
+
/**
|
|
790
|
+
* Strips wrapper expressions that do not change the underlying value: `await`,
|
|
791
|
+
* parentheses, `as`/`<T>`/`!`/`satisfies` type assertions, and
|
|
792
|
+
* `Promise.resolve(...)` (a synchronous callback may wrap its result in an
|
|
793
|
+
* already-resolved promise, which `MediaUploaderCallback` permits).
|
|
794
|
+
*/
|
|
795
|
+
function unwrapExpression(value) {
|
|
796
|
+
let current = toNode(value);
|
|
797
|
+
while (current != null) if (current.type === "AwaitExpression") current = toNode(current.argument);
|
|
798
|
+
else if (current.type === "TSAsExpression" || current.type === "TSNonNullExpression" || current.type === "TSSatisfiesExpression" || current.type === "TSTypeAssertion" || current.type === "ParenthesizedExpression") current = toNode(current.expression);
|
|
799
|
+
else if (isPromiseResolveCall(current)) {
|
|
800
|
+
const args = current.arguments;
|
|
801
|
+
current = Array.isArray(args) && args.length === 1 ? toNode(args[0]) : null;
|
|
802
|
+
} else return current;
|
|
803
|
+
return null;
|
|
804
|
+
}
|
|
805
|
+
/**
|
|
806
|
+
* Whether the expression is a member call `<ctx>.getObjectUri(...)` whose
|
|
807
|
+
* receiver is the callback's context parameter. A call on any other object
|
|
808
|
+
* (e.g. `helper.getObjectUri(...)`), or a bare `getObjectUri(...)` from a
|
|
809
|
+
* destructured parameter (which loses the receiver and throws at runtime),
|
|
810
|
+
* does not count.
|
|
811
|
+
*/
|
|
812
|
+
function isGetObjectUriCall(expr, ctx) {
|
|
813
|
+
if (expr.type !== "CallExpression") return false;
|
|
814
|
+
if (ctx.ctxName == null) return false;
|
|
815
|
+
const callee = toNode(expr.callee);
|
|
816
|
+
if (callee == null || callee.type !== "MemberExpression" || getMemberPropertyName$1(callee) !== GETTER_NAME) return false;
|
|
817
|
+
const object = unwrapExpression(callee.object);
|
|
818
|
+
return object != null && object.type === "Identifier" && object.name === ctx.ctxName;
|
|
819
|
+
}
|
|
820
|
+
function isUrlConstructor(expr) {
|
|
821
|
+
const callee = toNode(expr.callee);
|
|
822
|
+
return callee != null && callee.type === "Identifier" && callee.name === "URL";
|
|
823
|
+
}
|
|
824
|
+
/** Finds the first `ObjectExpression` argument of a `NewExpression`. */
|
|
825
|
+
function extractObjectArg(newExpr) {
|
|
826
|
+
const args = newExpr.arguments;
|
|
827
|
+
if (!Array.isArray(args)) return null;
|
|
828
|
+
for (const arg of args) {
|
|
829
|
+
const a = toNode(arg);
|
|
830
|
+
if (a != null && a.type === "ObjectExpression") return a;
|
|
831
|
+
}
|
|
832
|
+
return null;
|
|
833
|
+
}
|
|
834
|
+
/**
|
|
835
|
+
* Whether a URL-valued expression is derived from `getObjectUri()`: a direct
|
|
836
|
+
* call, an identifier whose local binding resolves to one
|
|
837
|
+
* (`const uri = ctx.getObjectUri(...); return uri`), both branches of a
|
|
838
|
+
* ternary, or `new URL(<derived>)`.
|
|
839
|
+
*/
|
|
840
|
+
function urlDerivesFromGetObjectUri(value, ctx, seen) {
|
|
841
|
+
const expr = unwrapExpression(value);
|
|
842
|
+
if (expr == null) return false;
|
|
843
|
+
if (isGetObjectUriCall(expr, ctx)) return true;
|
|
844
|
+
if (expr.type === "Identifier") {
|
|
845
|
+
const name = expr.name;
|
|
846
|
+
if (typeof name === "string" && ctx.bindings.has(name) && !seen.has(name)) return urlDerivesFromGetObjectUri(ctx.bindings.get(name), ctx, new Set(seen).add(name));
|
|
847
|
+
return false;
|
|
848
|
+
}
|
|
849
|
+
if (expr.type === "ConditionalExpression") return urlDerivesFromGetObjectUri(expr.consequent, ctx, seen) && urlDerivesFromGetObjectUri(expr.alternate, ctx, seen);
|
|
850
|
+
if (expr.type === "NewExpression" && isUrlConstructor(expr)) {
|
|
851
|
+
const args = expr.arguments;
|
|
852
|
+
return Array.isArray(args) && args.length > 0 && urlDerivesFromGetObjectUri(args[0], ctx, seen);
|
|
853
|
+
}
|
|
854
|
+
return false;
|
|
855
|
+
}
|
|
856
|
+
const SPREAD_TYPES = new Set([
|
|
857
|
+
"SpreadElement",
|
|
858
|
+
"SpreadProperty",
|
|
859
|
+
"ExperimentalSpreadProperty"
|
|
860
|
+
]);
|
|
861
|
+
/**
|
|
862
|
+
* Whether an object literal's *effective* own `id` is derived from
|
|
863
|
+
* `getObjectUri`. Properties are evaluated in order so a later duplicate `id`
|
|
864
|
+
* wins, and a spread or computed key appearing after the last static `id`
|
|
865
|
+
* (which could override it) is treated as unsafe. An object with no static
|
|
866
|
+
* `id` property (its `id` might come from an unresolved spread) is also
|
|
867
|
+
* unsafe.
|
|
868
|
+
*/
|
|
869
|
+
function objectIdDerivesFromGetObjectUri(objExpr, ctx, seen) {
|
|
870
|
+
const props = objExpr.properties;
|
|
871
|
+
if (!Array.isArray(props)) return false;
|
|
872
|
+
let idValue;
|
|
873
|
+
let idIsSet = false;
|
|
874
|
+
let overridableAfterId = false;
|
|
875
|
+
for (const prop of props) {
|
|
876
|
+
const p = toNode(prop);
|
|
877
|
+
if (p == null) return false;
|
|
878
|
+
if (SPREAD_TYPES.has(p.type) || p.type === "Property" && p.computed === true) {
|
|
879
|
+
if (idIsSet) overridableAfterId = true;
|
|
880
|
+
continue;
|
|
881
|
+
}
|
|
882
|
+
if (p.type !== "Property") continue;
|
|
883
|
+
const key = toNode(p.key);
|
|
884
|
+
if ((key == null ? null : key.type === "Identifier" && typeof key.name === "string" ? key.name : key.type === "Literal" && typeof key.value === "string" ? key.value : null) !== "id") continue;
|
|
885
|
+
idValue = p.value;
|
|
886
|
+
idIsSet = true;
|
|
887
|
+
overridableAfterId = false;
|
|
888
|
+
}
|
|
889
|
+
if (!idIsSet || overridableAfterId) return false;
|
|
890
|
+
return urlDerivesFromGetObjectUri(idValue, ctx, seen);
|
|
891
|
+
}
|
|
892
|
+
/**
|
|
893
|
+
* Whether a single returned expression is valid: a URL derived from
|
|
894
|
+
* `getObjectUri()`, or a vocab object (a `new X({...})` expression) whose own
|
|
895
|
+
* `id` is derived from it. The object is judged solely by its `id`, so
|
|
896
|
+
* `getObjectUri()` appearing in another property (e.g. `url`) does not excuse a
|
|
897
|
+
* hard-coded `id`. A plain object literal is rejected: the handler calls
|
|
898
|
+
* `toJsonLd()` on the result, so a bare `{ ... }` (which lacks it) is not a
|
|
899
|
+
* valid return.
|
|
900
|
+
*/
|
|
901
|
+
function returnDerivesFromGetObjectUri(value, ctx, seen) {
|
|
902
|
+
const expr = unwrapExpression(value);
|
|
903
|
+
if (expr == null) return false;
|
|
904
|
+
if (expr.type === "Identifier") {
|
|
905
|
+
const name = expr.name;
|
|
906
|
+
if (typeof name === "string" && ctx.bindings.has(name) && !seen.has(name)) return returnDerivesFromGetObjectUri(ctx.bindings.get(name), ctx, new Set(seen).add(name));
|
|
907
|
+
return false;
|
|
908
|
+
}
|
|
909
|
+
if (expr.type === "ConditionalExpression") return returnDerivesFromGetObjectUri(expr.consequent, ctx, seen) && returnDerivesFromGetObjectUri(expr.alternate, ctx, seen);
|
|
910
|
+
if (expr.type === "NewExpression") {
|
|
911
|
+
if (isUrlConstructor(expr)) return urlDerivesFromGetObjectUri(expr, ctx, seen);
|
|
912
|
+
const objArg = extractObjectArg(expr);
|
|
913
|
+
if (objArg == null) return false;
|
|
914
|
+
return objectIdDerivesFromGetObjectUri(objArg, ctx, seen);
|
|
915
|
+
}
|
|
916
|
+
return urlDerivesFromGetObjectUri(expr, ctx, seen);
|
|
917
|
+
}
|
|
918
|
+
/**
|
|
919
|
+
* Determines whether every returned value of the callback is derived from
|
|
920
|
+
* `getObjectUri`. Returns `false` when the callback has no return value or any
|
|
921
|
+
* returned value is not derived from `getObjectUri`.
|
|
922
|
+
*/
|
|
923
|
+
function callbackDerivesFromGetObjectUri(callback) {
|
|
924
|
+
const returnedExpressions = collectReturnedExpressions(callback);
|
|
925
|
+
if (returnedExpressions.length < 1) return false;
|
|
926
|
+
const bindings = /* @__PURE__ */ new Map();
|
|
927
|
+
collectLocalBindings(callback.body, bindings);
|
|
928
|
+
const ctx = {
|
|
929
|
+
ctxName: getContextParamName(callback),
|
|
930
|
+
bindings
|
|
931
|
+
};
|
|
932
|
+
return returnedExpressions.every((expr) => returnDerivesFromGetObjectUri(expr, ctx, /* @__PURE__ */ new Set()));
|
|
933
|
+
}
|
|
934
|
+
function createRule$1(buildReport) {
|
|
935
|
+
return (context) => {
|
|
936
|
+
const federationTracker = trackFederationVariables();
|
|
937
|
+
const bindings = /* @__PURE__ */ new Map();
|
|
938
|
+
const pendingCalls = [];
|
|
939
|
+
const inspectCall = (node) => {
|
|
940
|
+
if (!hasMemberExpressionCallee(node) || !hasIdentifierProperty(node) || !hasMethodName("setMediaUploader")(node) || node.arguments.length < 2) return;
|
|
941
|
+
if (!federationTracker.isFederationObject(node.callee.object)) return;
|
|
942
|
+
const callbackArg = node.arguments[1];
|
|
943
|
+
const callback = isNode(callbackArg) && isFunction(callbackArg) ? callbackArg : isNode(callbackArg) ? resolveCallbackReference(callbackArg, bindings) : null;
|
|
944
|
+
if (callback == null) return;
|
|
945
|
+
if (callbackDerivesFromGetObjectUri(callback)) return;
|
|
946
|
+
context.report({
|
|
947
|
+
node: callback,
|
|
948
|
+
...buildReport
|
|
949
|
+
});
|
|
950
|
+
};
|
|
951
|
+
return {
|
|
952
|
+
VariableDeclarator(node) {
|
|
953
|
+
federationTracker.VariableDeclarator(node);
|
|
954
|
+
if (node.id.type === "Identifier" && node.init != null) bindings.set(node.id.name, node.init);
|
|
955
|
+
},
|
|
956
|
+
FunctionDeclaration(node) {
|
|
957
|
+
if (node.id != null) bindings.set(node.id.name, node);
|
|
958
|
+
},
|
|
959
|
+
CallExpression(node) {
|
|
960
|
+
pendingCalls.push(node);
|
|
961
|
+
},
|
|
962
|
+
"Program:exit"() {
|
|
963
|
+
for (const node of pendingCalls) inspectCall(node);
|
|
964
|
+
}
|
|
965
|
+
};
|
|
966
|
+
};
|
|
967
|
+
}
|
|
968
|
+
const eslint$1 = {
|
|
969
|
+
meta: {
|
|
970
|
+
type: "suggestion",
|
|
971
|
+
docs: { description: "Warn when a setMediaUploader() callback does not derive its return value from ctx.getObjectUri()" },
|
|
972
|
+
schema: [],
|
|
973
|
+
messages: { required: "{{ message }}" }
|
|
974
|
+
},
|
|
975
|
+
create: createRule$1({
|
|
976
|
+
messageId: "required",
|
|
977
|
+
data: { message: MESSAGE$1 }
|
|
978
|
+
})
|
|
979
|
+
};
|
|
980
|
+
//#endregion
|
|
570
981
|
//#region src/rules/outbox-listener-delivery-required.ts
|
|
571
982
|
const MESSAGE = "Outbox listeners should deliver posted activities explicitly with ctx.sendActivity() or ctx.forwardActivity().";
|
|
572
983
|
const isChainedFromOutboxListeners = (expr, federationTracker) => {
|
|
@@ -819,4 +1230,4 @@ const eslint = {
|
|
|
819
1230
|
})
|
|
820
1231
|
};
|
|
821
1232
|
//#endregion
|
|
822
|
-
export {
|
|
1233
|
+
export { eslint$22 as C, RULE_IDS as D, eslint$25 as E, name as O, eslint$21 as S, eslint$24 as T, eslint$16 as _, eslint$4 as a, eslint$19 as b, eslint$7 as c, eslint$10 as d, eslint$11 as f, eslint$15 as g, eslint$14 as h, eslint$3 as i, version as k, eslint$8 as l, eslint$13 as m, eslint$1 as n, eslint$5 as o, eslint$12 as p, eslint$2 as r, eslint$6 as s, eslint as t, eslint$9 as u, eslint$17 as v, eslint$23 as w, eslint$20 as x, eslint$18 as y };
|
|
@@ -2,7 +2,7 @@ require("./index.cjs");
|
|
|
2
2
|
let _fxts_core = require("@fxts/core");
|
|
3
3
|
//#region deno.json
|
|
4
4
|
var name = "@fedify/lint";
|
|
5
|
-
var version = "2.4.0-dev.
|
|
5
|
+
var version = "2.4.0-dev.1634+3b4bfbb6";
|
|
6
6
|
//#endregion
|
|
7
7
|
//#region src/lib/const.ts
|
|
8
8
|
/**
|
|
@@ -78,6 +78,17 @@ const properties = {
|
|
|
78
78
|
wrapper: "Endpoints"
|
|
79
79
|
}
|
|
80
80
|
},
|
|
81
|
+
uploadMedia: {
|
|
82
|
+
name: "uploadMedia",
|
|
83
|
+
path: ["endpoints", "uploadMedia"],
|
|
84
|
+
getter: "getMediaUploaderUri",
|
|
85
|
+
setter: "setMediaUploader",
|
|
86
|
+
requiresIdentifier: true,
|
|
87
|
+
nested: {
|
|
88
|
+
parent: "endpoints",
|
|
89
|
+
wrapper: "Endpoints"
|
|
90
|
+
}
|
|
91
|
+
},
|
|
81
92
|
publicKey: {
|
|
82
93
|
name: "publicKey",
|
|
83
94
|
path: ["publicKey"],
|
|
@@ -108,6 +119,7 @@ const RULE_IDS = {
|
|
|
108
119
|
actorFeaturedTagsPropertyRequired: "actor-featured-tags-property-required",
|
|
109
120
|
actorInboxPropertyRequired: "actor-inbox-property-required",
|
|
110
121
|
actorSharedInboxPropertyRequired: "actor-shared-inbox-property-required",
|
|
122
|
+
actorUploadMediaPropertyRequired: "actor-upload-media-property-required",
|
|
111
123
|
actorPublicKeyRequired: "actor-public-key-required",
|
|
112
124
|
actorAssertionMethodRequired: "actor-assertion-method-required",
|
|
113
125
|
actorIdMismatch: "actor-id-mismatch",
|
|
@@ -119,8 +131,11 @@ const RULE_IDS = {
|
|
|
119
131
|
actorFeaturedTagsPropertyMismatch: "actor-featured-tags-property-mismatch",
|
|
120
132
|
actorInboxPropertyMismatch: "actor-inbox-property-mismatch",
|
|
121
133
|
actorSharedInboxPropertyMismatch: "actor-shared-inbox-property-mismatch",
|
|
134
|
+
actorUploadMediaPropertyMismatch: "actor-upload-media-property-mismatch",
|
|
122
135
|
collectionFilteringNotImplemented: "collection-filtering-not-implemented",
|
|
123
|
-
outboxListenerDeliveryRequired: "outbox-listener-delivery-required"
|
|
136
|
+
outboxListenerDeliveryRequired: "outbox-listener-delivery-required",
|
|
137
|
+
mediaUploaderObjectUriRequired: "media-uploader-object-uri-required",
|
|
138
|
+
mediaUploaderAuthorizationRequired: "media-uploader-authorization-required"
|
|
124
139
|
};
|
|
125
140
|
//#endregion
|
|
126
141
|
//#region src/lib/messages.ts
|
|
@@ -418,7 +433,7 @@ function createRequiredRuleEslint(config) {
|
|
|
418
433
|
};
|
|
419
434
|
}
|
|
420
435
|
createRequiredRuleDeno(properties.assertionMethod);
|
|
421
|
-
const eslint$
|
|
436
|
+
const eslint$25 = createRequiredRuleEslint(properties.assertionMethod);
|
|
422
437
|
//#endregion
|
|
423
438
|
//#region src/lib/mismatch.ts
|
|
424
439
|
const isIdentifierWithName = (name) => (node) => allOf(isNodeType("Identifier"), isNodeName(name))(node);
|
|
@@ -488,43 +503,47 @@ const createMismatchRuleEslint = (config) => ({
|
|
|
488
503
|
}))
|
|
489
504
|
});
|
|
490
505
|
createMismatchRuleDeno(properties.featured);
|
|
491
|
-
const eslint$
|
|
506
|
+
const eslint$24 = createMismatchRuleEslint(properties.featured);
|
|
492
507
|
createRequiredRuleDeno(properties.featured);
|
|
493
|
-
const eslint$
|
|
508
|
+
const eslint$23 = createRequiredRuleEslint(properties.featured);
|
|
494
509
|
createMismatchRuleDeno(properties.featuredTags);
|
|
495
|
-
const eslint$
|
|
510
|
+
const eslint$22 = createMismatchRuleEslint(properties.featuredTags);
|
|
496
511
|
createRequiredRuleDeno(properties.featuredTags);
|
|
497
|
-
const eslint$
|
|
512
|
+
const eslint$21 = createRequiredRuleEslint(properties.featuredTags);
|
|
498
513
|
createMismatchRuleDeno(properties.followers);
|
|
499
|
-
const eslint$
|
|
514
|
+
const eslint$20 = createMismatchRuleEslint(properties.followers);
|
|
500
515
|
createRequiredRuleDeno(properties.followers);
|
|
501
|
-
const eslint$
|
|
516
|
+
const eslint$19 = createRequiredRuleEslint(properties.followers);
|
|
502
517
|
createMismatchRuleDeno(properties.following);
|
|
503
|
-
const eslint$
|
|
518
|
+
const eslint$18 = createMismatchRuleEslint(properties.following);
|
|
504
519
|
createRequiredRuleDeno(properties.following);
|
|
505
|
-
const eslint$
|
|
520
|
+
const eslint$17 = createRequiredRuleEslint(properties.following);
|
|
506
521
|
createMismatchRuleDeno(properties.id);
|
|
507
|
-
const eslint$
|
|
522
|
+
const eslint$16 = createMismatchRuleEslint(properties.id);
|
|
508
523
|
createRequiredRuleDeno(properties.id);
|
|
509
|
-
const eslint$
|
|
524
|
+
const eslint$15 = createRequiredRuleEslint(properties.id);
|
|
510
525
|
createMismatchRuleDeno(properties.inbox);
|
|
511
|
-
const eslint$
|
|
526
|
+
const eslint$14 = createMismatchRuleEslint(properties.inbox);
|
|
512
527
|
createRequiredRuleDeno(properties.inbox);
|
|
513
|
-
const eslint$
|
|
528
|
+
const eslint$13 = createRequiredRuleEslint(properties.inbox);
|
|
514
529
|
createMismatchRuleDeno(properties.liked);
|
|
515
|
-
const eslint$
|
|
530
|
+
const eslint$12 = createMismatchRuleEslint(properties.liked);
|
|
516
531
|
createRequiredRuleDeno(properties.liked);
|
|
517
|
-
const eslint$
|
|
532
|
+
const eslint$11 = createRequiredRuleEslint(properties.liked);
|
|
518
533
|
createMismatchRuleDeno(properties.outbox);
|
|
519
|
-
const eslint$
|
|
534
|
+
const eslint$10 = createMismatchRuleEslint(properties.outbox);
|
|
520
535
|
createRequiredRuleDeno(properties.outbox);
|
|
521
|
-
const eslint$
|
|
536
|
+
const eslint$9 = createRequiredRuleEslint(properties.outbox);
|
|
522
537
|
createRequiredRuleDeno(properties.publicKey);
|
|
523
|
-
const eslint$
|
|
538
|
+
const eslint$8 = createRequiredRuleEslint(properties.publicKey);
|
|
524
539
|
createMismatchRuleDeno(properties.sharedInbox);
|
|
525
|
-
const eslint$
|
|
540
|
+
const eslint$7 = createMismatchRuleEslint(properties.sharedInbox);
|
|
526
541
|
createRequiredRuleDeno(properties.sharedInbox);
|
|
527
|
-
const eslint$
|
|
542
|
+
const eslint$6 = createRequiredRuleEslint(properties.sharedInbox);
|
|
543
|
+
createMismatchRuleDeno(properties.uploadMedia);
|
|
544
|
+
const eslint$5 = createMismatchRuleEslint(properties.uploadMedia);
|
|
545
|
+
createRequiredRuleDeno(properties.uploadMedia);
|
|
546
|
+
const eslint$4 = createRequiredRuleEslint(properties.uploadMedia);
|
|
528
547
|
//#endregion
|
|
529
548
|
//#region src/rules/collection-filtering-not-implemented.ts
|
|
530
549
|
/**
|
|
@@ -543,7 +562,7 @@ const isFollowersDispatcherCall = (node) => "callee" in node && node.callee && n
|
|
|
543
562
|
* CollectionDispatcher signature: (context, identifier, cursor, filter?) => ...
|
|
544
563
|
*/
|
|
545
564
|
const hasFilterParameter = hasMinParams(4);
|
|
546
|
-
const eslint$
|
|
565
|
+
const eslint$3 = {
|
|
547
566
|
meta: {
|
|
548
567
|
type: "suggestion",
|
|
549
568
|
docs: { description: "Ensure followers dispatcher implements filtering" },
|
|
@@ -568,6 +587,398 @@ const eslint$1 = {
|
|
|
568
587
|
}
|
|
569
588
|
};
|
|
570
589
|
//#endregion
|
|
590
|
+
//#region src/rules/media-uploader-authorization-required.ts
|
|
591
|
+
const MESSAGE$2 = "setMediaUploader() should be protected with .authorize(); without it the endpoint accepts uploads from anyone who can reach the URL. Add .authorize() unless a public upload endpoint is intended.";
|
|
592
|
+
function getParent(node) {
|
|
593
|
+
if (node == null || typeof node !== "object") return null;
|
|
594
|
+
const parent = node.parent;
|
|
595
|
+
return isNode(parent) ? parent : null;
|
|
596
|
+
}
|
|
597
|
+
/**
|
|
598
|
+
* Whether the given `setMediaUploader(...)` call is directly chained with
|
|
599
|
+
* `.authorize(...)`, i.e. `federation.setMediaUploader(...).authorize(...)`.
|
|
600
|
+
*/
|
|
601
|
+
function isDirectlyAuthorized(callNode) {
|
|
602
|
+
const member = getParent(callNode);
|
|
603
|
+
if (member == null || member.type !== "MemberExpression" || member.computed === true) return false;
|
|
604
|
+
const property = member.property;
|
|
605
|
+
if (!isNode(property) || property.type !== "Identifier" || property.name !== "authorize") return false;
|
|
606
|
+
const call = getParent(member);
|
|
607
|
+
return call != null && call.type === "CallExpression" && call.callee === member;
|
|
608
|
+
}
|
|
609
|
+
function createRule$2(buildReport) {
|
|
610
|
+
return (context) => {
|
|
611
|
+
const federationTracker = trackFederationVariables();
|
|
612
|
+
const unchained = [];
|
|
613
|
+
const authorizedVars = /* @__PURE__ */ new Set();
|
|
614
|
+
return {
|
|
615
|
+
VariableDeclarator: federationTracker.VariableDeclarator,
|
|
616
|
+
CallExpression(node) {
|
|
617
|
+
if (hasMemberExpressionCallee(node) && hasIdentifierProperty(node) && hasMethodName("authorize")(node)) {
|
|
618
|
+
const object = node.callee.object;
|
|
619
|
+
if (isNode(object) && object.type === "Identifier") authorizedVars.add(object.name);
|
|
620
|
+
return;
|
|
621
|
+
}
|
|
622
|
+
if (!hasMemberExpressionCallee(node) || !hasIdentifierProperty(node) || !hasMethodName("setMediaUploader")(node) || !federationTracker.isFederationObject(node.callee.object)) return;
|
|
623
|
+
if (isDirectlyAuthorized(node)) return;
|
|
624
|
+
const parent = getParent(node);
|
|
625
|
+
let varName = null;
|
|
626
|
+
if (parent != null && parent.type === "VariableDeclarator") {
|
|
627
|
+
const id = parent.id;
|
|
628
|
+
if (isNode(id) && id.type === "Identifier") varName = id.name;
|
|
629
|
+
}
|
|
630
|
+
unchained.push({
|
|
631
|
+
node,
|
|
632
|
+
varName
|
|
633
|
+
});
|
|
634
|
+
},
|
|
635
|
+
"Program:exit"() {
|
|
636
|
+
for (const { node, varName } of unchained) {
|
|
637
|
+
if (varName != null && authorizedVars.has(varName)) continue;
|
|
638
|
+
context.report({
|
|
639
|
+
node,
|
|
640
|
+
...buildReport
|
|
641
|
+
});
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
};
|
|
645
|
+
};
|
|
646
|
+
}
|
|
647
|
+
const eslint$2 = {
|
|
648
|
+
meta: {
|
|
649
|
+
type: "suggestion",
|
|
650
|
+
docs: { description: "Warn when setMediaUploader() is registered without an authorize() hook" },
|
|
651
|
+
schema: [],
|
|
652
|
+
messages: { required: "{{ message }}" }
|
|
653
|
+
},
|
|
654
|
+
create: createRule$2({
|
|
655
|
+
messageId: "required",
|
|
656
|
+
data: { message: MESSAGE$2 }
|
|
657
|
+
})
|
|
658
|
+
};
|
|
659
|
+
//#endregion
|
|
660
|
+
//#region src/rules/media-uploader-object-uri-required.ts
|
|
661
|
+
const MESSAGE$1 = "setMediaUploader() callbacks should return a value derived from ctx.getObjectUri(): either return ctx.getObjectUri(...) directly, or an object whose id is ctx.getObjectUri(...).";
|
|
662
|
+
const GETTER_NAME = "getObjectUri";
|
|
663
|
+
const getMemberPropertyName$1 = (node) => {
|
|
664
|
+
if (node.type !== "MemberExpression") return null;
|
|
665
|
+
const property = node.property;
|
|
666
|
+
if (property.type === "Identifier") return property.name;
|
|
667
|
+
if (property.type === "Literal" && typeof property.value === "string") return property.value;
|
|
668
|
+
return null;
|
|
669
|
+
};
|
|
670
|
+
function unwrapAssignmentPattern(node) {
|
|
671
|
+
let current = node ?? null;
|
|
672
|
+
while (current?.type === "AssignmentPattern") current = current.left;
|
|
673
|
+
return current;
|
|
674
|
+
}
|
|
675
|
+
/**
|
|
676
|
+
* Resolves the callback argument to a function node, following simple bindings
|
|
677
|
+
* when the callback is passed by reference (identifier or object member).
|
|
678
|
+
*/
|
|
679
|
+
const resolveCallbackReference = (expr, bindings, seen = /* @__PURE__ */ new Set()) => {
|
|
680
|
+
if (isFunction(expr)) return expr;
|
|
681
|
+
if (expr.type === "Identifier") {
|
|
682
|
+
if (seen.has(expr.name)) return null;
|
|
683
|
+
seen.add(expr.name);
|
|
684
|
+
const binding = bindings.get(expr.name);
|
|
685
|
+
if (binding == null || !isNode(binding)) return null;
|
|
686
|
+
if (isFunction(binding) || binding.type === "FunctionDeclaration") return binding;
|
|
687
|
+
if (binding.type === "Identifier") return resolveCallbackReference(binding, bindings, seen);
|
|
688
|
+
return null;
|
|
689
|
+
}
|
|
690
|
+
if (expr.type === "MemberExpression" && expr.object.type === "Identifier" && !expr.computed) {
|
|
691
|
+
const binding = bindings.get(expr.object.name);
|
|
692
|
+
if (binding == null || !isNode(binding) || binding.type !== "ObjectExpression") return null;
|
|
693
|
+
const propertyName = getMemberPropertyName$1(expr);
|
|
694
|
+
if (propertyName == null) return null;
|
|
695
|
+
for (const prop of binding.properties) {
|
|
696
|
+
if (!isNode(prop) || prop.type !== "Property") continue;
|
|
697
|
+
if ((prop.key.type === "Identifier" ? prop.key.name : prop.key.type === "Literal" && typeof prop.key.value === "string" ? prop.key.value : null) !== propertyName || !isNode(prop.value)) continue;
|
|
698
|
+
const value = prop.value;
|
|
699
|
+
if (isFunction(value) || value.type === "FunctionDeclaration") return value;
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
return null;
|
|
703
|
+
};
|
|
704
|
+
/**
|
|
705
|
+
* Returns the identifier name of the callback's context parameter, used to
|
|
706
|
+
* require that `getObjectUri()` is called as a member of the context itself
|
|
707
|
+
* (`ctx.getObjectUri(...)`), not on some other object. Returns `null` when the
|
|
708
|
+
* parameter is destructured: destructuring `getObjectUri` off the context
|
|
709
|
+
* (`({ getObjectUri }) => getObjectUri(...)`) loses the method's receiver and
|
|
710
|
+
* throws at runtime, so that form is intentionally not recognized as valid.
|
|
711
|
+
*/
|
|
712
|
+
function getContextParamName(callback) {
|
|
713
|
+
const contextParam = unwrapAssignmentPattern(callback.params[0]);
|
|
714
|
+
return contextParam?.type === "Identifier" ? contextParam.name : null;
|
|
715
|
+
}
|
|
716
|
+
const FUNCTION_NODE_TYPES = new Set([
|
|
717
|
+
"FunctionDeclaration",
|
|
718
|
+
"FunctionExpression",
|
|
719
|
+
"ArrowFunctionExpression"
|
|
720
|
+
]);
|
|
721
|
+
/**
|
|
722
|
+
* Collects `const`/`let`/`var name = init` bindings declared inside the
|
|
723
|
+
* callback body (excluding nested functions), so an identifier used in a
|
|
724
|
+
* returned expression can be resolved back to the expression it was assigned.
|
|
725
|
+
*/
|
|
726
|
+
function collectLocalBindings(node, bindings) {
|
|
727
|
+
if (node == null || typeof node !== "object") return;
|
|
728
|
+
if (Array.isArray(node)) {
|
|
729
|
+
for (const child of node) collectLocalBindings(child, bindings);
|
|
730
|
+
return;
|
|
731
|
+
}
|
|
732
|
+
const n = node;
|
|
733
|
+
if (typeof n.type !== "string") return;
|
|
734
|
+
if (FUNCTION_NODE_TYPES.has(n.type)) return;
|
|
735
|
+
if (n.type === "VariableDeclarator") {
|
|
736
|
+
const id = n.id;
|
|
737
|
+
if (id != null && isNode(id) && id.type === "Identifier" && n.init != null) {
|
|
738
|
+
if (!bindings.has(id.name)) bindings.set(id.name, n.init);
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
for (const key in n) {
|
|
742
|
+
if (key === "parent") continue;
|
|
743
|
+
collectLocalBindings(n[key], bindings);
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
/**
|
|
747
|
+
* Collects the returned expressions of the callback: the argument of every
|
|
748
|
+
* `return` statement (excluding nested functions), or, for an
|
|
749
|
+
* expression-bodied arrow, the body expression itself. A bare `return;`
|
|
750
|
+
* contributes `null`.
|
|
751
|
+
*/
|
|
752
|
+
function collectReturnedExpressions(callback) {
|
|
753
|
+
const body = callback.body;
|
|
754
|
+
if (body != null && isNode(body) && body.type !== "BlockStatement") return [body];
|
|
755
|
+
const result = [];
|
|
756
|
+
const walk = (node) => {
|
|
757
|
+
if (node == null || typeof node !== "object") return;
|
|
758
|
+
if (Array.isArray(node)) {
|
|
759
|
+
for (const child of node) walk(child);
|
|
760
|
+
return;
|
|
761
|
+
}
|
|
762
|
+
const n = node;
|
|
763
|
+
if (typeof n.type !== "string") return;
|
|
764
|
+
if (n.type === "ReturnStatement") {
|
|
765
|
+
result.push(n.argument ?? null);
|
|
766
|
+
return;
|
|
767
|
+
}
|
|
768
|
+
if (FUNCTION_NODE_TYPES.has(n.type)) return;
|
|
769
|
+
for (const key in n) {
|
|
770
|
+
if (key === "parent") continue;
|
|
771
|
+
walk(n[key]);
|
|
772
|
+
}
|
|
773
|
+
};
|
|
774
|
+
walk(body);
|
|
775
|
+
return result;
|
|
776
|
+
}
|
|
777
|
+
function toNode(value) {
|
|
778
|
+
if (value == null || typeof value !== "object" || Array.isArray(value)) return null;
|
|
779
|
+
const n = value;
|
|
780
|
+
return typeof n.type === "string" ? n : null;
|
|
781
|
+
}
|
|
782
|
+
/** Whether the node is a `Promise.resolve(x)` call. */
|
|
783
|
+
function isPromiseResolveCall(expr) {
|
|
784
|
+
if (expr.type !== "CallExpression") return false;
|
|
785
|
+
const callee = toNode(expr.callee);
|
|
786
|
+
if (callee == null || callee.type !== "MemberExpression" || getMemberPropertyName$1(callee) !== "resolve") return false;
|
|
787
|
+
const object = toNode(callee.object);
|
|
788
|
+
return object != null && object.type === "Identifier" && object.name === "Promise";
|
|
789
|
+
}
|
|
790
|
+
/**
|
|
791
|
+
* Strips wrapper expressions that do not change the underlying value: `await`,
|
|
792
|
+
* parentheses, `as`/`<T>`/`!`/`satisfies` type assertions, and
|
|
793
|
+
* `Promise.resolve(...)` (a synchronous callback may wrap its result in an
|
|
794
|
+
* already-resolved promise, which `MediaUploaderCallback` permits).
|
|
795
|
+
*/
|
|
796
|
+
function unwrapExpression(value) {
|
|
797
|
+
let current = toNode(value);
|
|
798
|
+
while (current != null) if (current.type === "AwaitExpression") current = toNode(current.argument);
|
|
799
|
+
else if (current.type === "TSAsExpression" || current.type === "TSNonNullExpression" || current.type === "TSSatisfiesExpression" || current.type === "TSTypeAssertion" || current.type === "ParenthesizedExpression") current = toNode(current.expression);
|
|
800
|
+
else if (isPromiseResolveCall(current)) {
|
|
801
|
+
const args = current.arguments;
|
|
802
|
+
current = Array.isArray(args) && args.length === 1 ? toNode(args[0]) : null;
|
|
803
|
+
} else return current;
|
|
804
|
+
return null;
|
|
805
|
+
}
|
|
806
|
+
/**
|
|
807
|
+
* Whether the expression is a member call `<ctx>.getObjectUri(...)` whose
|
|
808
|
+
* receiver is the callback's context parameter. A call on any other object
|
|
809
|
+
* (e.g. `helper.getObjectUri(...)`), or a bare `getObjectUri(...)` from a
|
|
810
|
+
* destructured parameter (which loses the receiver and throws at runtime),
|
|
811
|
+
* does not count.
|
|
812
|
+
*/
|
|
813
|
+
function isGetObjectUriCall(expr, ctx) {
|
|
814
|
+
if (expr.type !== "CallExpression") return false;
|
|
815
|
+
if (ctx.ctxName == null) return false;
|
|
816
|
+
const callee = toNode(expr.callee);
|
|
817
|
+
if (callee == null || callee.type !== "MemberExpression" || getMemberPropertyName$1(callee) !== GETTER_NAME) return false;
|
|
818
|
+
const object = unwrapExpression(callee.object);
|
|
819
|
+
return object != null && object.type === "Identifier" && object.name === ctx.ctxName;
|
|
820
|
+
}
|
|
821
|
+
function isUrlConstructor(expr) {
|
|
822
|
+
const callee = toNode(expr.callee);
|
|
823
|
+
return callee != null && callee.type === "Identifier" && callee.name === "URL";
|
|
824
|
+
}
|
|
825
|
+
/** Finds the first `ObjectExpression` argument of a `NewExpression`. */
|
|
826
|
+
function extractObjectArg(newExpr) {
|
|
827
|
+
const args = newExpr.arguments;
|
|
828
|
+
if (!Array.isArray(args)) return null;
|
|
829
|
+
for (const arg of args) {
|
|
830
|
+
const a = toNode(arg);
|
|
831
|
+
if (a != null && a.type === "ObjectExpression") return a;
|
|
832
|
+
}
|
|
833
|
+
return null;
|
|
834
|
+
}
|
|
835
|
+
/**
|
|
836
|
+
* Whether a URL-valued expression is derived from `getObjectUri()`: a direct
|
|
837
|
+
* call, an identifier whose local binding resolves to one
|
|
838
|
+
* (`const uri = ctx.getObjectUri(...); return uri`), both branches of a
|
|
839
|
+
* ternary, or `new URL(<derived>)`.
|
|
840
|
+
*/
|
|
841
|
+
function urlDerivesFromGetObjectUri(value, ctx, seen) {
|
|
842
|
+
const expr = unwrapExpression(value);
|
|
843
|
+
if (expr == null) return false;
|
|
844
|
+
if (isGetObjectUriCall(expr, ctx)) return true;
|
|
845
|
+
if (expr.type === "Identifier") {
|
|
846
|
+
const name = expr.name;
|
|
847
|
+
if (typeof name === "string" && ctx.bindings.has(name) && !seen.has(name)) return urlDerivesFromGetObjectUri(ctx.bindings.get(name), ctx, new Set(seen).add(name));
|
|
848
|
+
return false;
|
|
849
|
+
}
|
|
850
|
+
if (expr.type === "ConditionalExpression") return urlDerivesFromGetObjectUri(expr.consequent, ctx, seen) && urlDerivesFromGetObjectUri(expr.alternate, ctx, seen);
|
|
851
|
+
if (expr.type === "NewExpression" && isUrlConstructor(expr)) {
|
|
852
|
+
const args = expr.arguments;
|
|
853
|
+
return Array.isArray(args) && args.length > 0 && urlDerivesFromGetObjectUri(args[0], ctx, seen);
|
|
854
|
+
}
|
|
855
|
+
return false;
|
|
856
|
+
}
|
|
857
|
+
const SPREAD_TYPES = new Set([
|
|
858
|
+
"SpreadElement",
|
|
859
|
+
"SpreadProperty",
|
|
860
|
+
"ExperimentalSpreadProperty"
|
|
861
|
+
]);
|
|
862
|
+
/**
|
|
863
|
+
* Whether an object literal's *effective* own `id` is derived from
|
|
864
|
+
* `getObjectUri`. Properties are evaluated in order so a later duplicate `id`
|
|
865
|
+
* wins, and a spread or computed key appearing after the last static `id`
|
|
866
|
+
* (which could override it) is treated as unsafe. An object with no static
|
|
867
|
+
* `id` property (its `id` might come from an unresolved spread) is also
|
|
868
|
+
* unsafe.
|
|
869
|
+
*/
|
|
870
|
+
function objectIdDerivesFromGetObjectUri(objExpr, ctx, seen) {
|
|
871
|
+
const props = objExpr.properties;
|
|
872
|
+
if (!Array.isArray(props)) return false;
|
|
873
|
+
let idValue;
|
|
874
|
+
let idIsSet = false;
|
|
875
|
+
let overridableAfterId = false;
|
|
876
|
+
for (const prop of props) {
|
|
877
|
+
const p = toNode(prop);
|
|
878
|
+
if (p == null) return false;
|
|
879
|
+
if (SPREAD_TYPES.has(p.type) || p.type === "Property" && p.computed === true) {
|
|
880
|
+
if (idIsSet) overridableAfterId = true;
|
|
881
|
+
continue;
|
|
882
|
+
}
|
|
883
|
+
if (p.type !== "Property") continue;
|
|
884
|
+
const key = toNode(p.key);
|
|
885
|
+
if ((key == null ? null : key.type === "Identifier" && typeof key.name === "string" ? key.name : key.type === "Literal" && typeof key.value === "string" ? key.value : null) !== "id") continue;
|
|
886
|
+
idValue = p.value;
|
|
887
|
+
idIsSet = true;
|
|
888
|
+
overridableAfterId = false;
|
|
889
|
+
}
|
|
890
|
+
if (!idIsSet || overridableAfterId) return false;
|
|
891
|
+
return urlDerivesFromGetObjectUri(idValue, ctx, seen);
|
|
892
|
+
}
|
|
893
|
+
/**
|
|
894
|
+
* Whether a single returned expression is valid: a URL derived from
|
|
895
|
+
* `getObjectUri()`, or a vocab object (a `new X({...})` expression) whose own
|
|
896
|
+
* `id` is derived from it. The object is judged solely by its `id`, so
|
|
897
|
+
* `getObjectUri()` appearing in another property (e.g. `url`) does not excuse a
|
|
898
|
+
* hard-coded `id`. A plain object literal is rejected: the handler calls
|
|
899
|
+
* `toJsonLd()` on the result, so a bare `{ ... }` (which lacks it) is not a
|
|
900
|
+
* valid return.
|
|
901
|
+
*/
|
|
902
|
+
function returnDerivesFromGetObjectUri(value, ctx, seen) {
|
|
903
|
+
const expr = unwrapExpression(value);
|
|
904
|
+
if (expr == null) return false;
|
|
905
|
+
if (expr.type === "Identifier") {
|
|
906
|
+
const name = expr.name;
|
|
907
|
+
if (typeof name === "string" && ctx.bindings.has(name) && !seen.has(name)) return returnDerivesFromGetObjectUri(ctx.bindings.get(name), ctx, new Set(seen).add(name));
|
|
908
|
+
return false;
|
|
909
|
+
}
|
|
910
|
+
if (expr.type === "ConditionalExpression") return returnDerivesFromGetObjectUri(expr.consequent, ctx, seen) && returnDerivesFromGetObjectUri(expr.alternate, ctx, seen);
|
|
911
|
+
if (expr.type === "NewExpression") {
|
|
912
|
+
if (isUrlConstructor(expr)) return urlDerivesFromGetObjectUri(expr, ctx, seen);
|
|
913
|
+
const objArg = extractObjectArg(expr);
|
|
914
|
+
if (objArg == null) return false;
|
|
915
|
+
return objectIdDerivesFromGetObjectUri(objArg, ctx, seen);
|
|
916
|
+
}
|
|
917
|
+
return urlDerivesFromGetObjectUri(expr, ctx, seen);
|
|
918
|
+
}
|
|
919
|
+
/**
|
|
920
|
+
* Determines whether every returned value of the callback is derived from
|
|
921
|
+
* `getObjectUri`. Returns `false` when the callback has no return value or any
|
|
922
|
+
* returned value is not derived from `getObjectUri`.
|
|
923
|
+
*/
|
|
924
|
+
function callbackDerivesFromGetObjectUri(callback) {
|
|
925
|
+
const returnedExpressions = collectReturnedExpressions(callback);
|
|
926
|
+
if (returnedExpressions.length < 1) return false;
|
|
927
|
+
const bindings = /* @__PURE__ */ new Map();
|
|
928
|
+
collectLocalBindings(callback.body, bindings);
|
|
929
|
+
const ctx = {
|
|
930
|
+
ctxName: getContextParamName(callback),
|
|
931
|
+
bindings
|
|
932
|
+
};
|
|
933
|
+
return returnedExpressions.every((expr) => returnDerivesFromGetObjectUri(expr, ctx, /* @__PURE__ */ new Set()));
|
|
934
|
+
}
|
|
935
|
+
function createRule$1(buildReport) {
|
|
936
|
+
return (context) => {
|
|
937
|
+
const federationTracker = trackFederationVariables();
|
|
938
|
+
const bindings = /* @__PURE__ */ new Map();
|
|
939
|
+
const pendingCalls = [];
|
|
940
|
+
const inspectCall = (node) => {
|
|
941
|
+
if (!hasMemberExpressionCallee(node) || !hasIdentifierProperty(node) || !hasMethodName("setMediaUploader")(node) || node.arguments.length < 2) return;
|
|
942
|
+
if (!federationTracker.isFederationObject(node.callee.object)) return;
|
|
943
|
+
const callbackArg = node.arguments[1];
|
|
944
|
+
const callback = isNode(callbackArg) && isFunction(callbackArg) ? callbackArg : isNode(callbackArg) ? resolveCallbackReference(callbackArg, bindings) : null;
|
|
945
|
+
if (callback == null) return;
|
|
946
|
+
if (callbackDerivesFromGetObjectUri(callback)) return;
|
|
947
|
+
context.report({
|
|
948
|
+
node: callback,
|
|
949
|
+
...buildReport
|
|
950
|
+
});
|
|
951
|
+
};
|
|
952
|
+
return {
|
|
953
|
+
VariableDeclarator(node) {
|
|
954
|
+
federationTracker.VariableDeclarator(node);
|
|
955
|
+
if (node.id.type === "Identifier" && node.init != null) bindings.set(node.id.name, node.init);
|
|
956
|
+
},
|
|
957
|
+
FunctionDeclaration(node) {
|
|
958
|
+
if (node.id != null) bindings.set(node.id.name, node);
|
|
959
|
+
},
|
|
960
|
+
CallExpression(node) {
|
|
961
|
+
pendingCalls.push(node);
|
|
962
|
+
},
|
|
963
|
+
"Program:exit"() {
|
|
964
|
+
for (const node of pendingCalls) inspectCall(node);
|
|
965
|
+
}
|
|
966
|
+
};
|
|
967
|
+
};
|
|
968
|
+
}
|
|
969
|
+
const eslint$1 = {
|
|
970
|
+
meta: {
|
|
971
|
+
type: "suggestion",
|
|
972
|
+
docs: { description: "Warn when a setMediaUploader() callback does not derive its return value from ctx.getObjectUri()" },
|
|
973
|
+
schema: [],
|
|
974
|
+
messages: { required: "{{ message }}" }
|
|
975
|
+
},
|
|
976
|
+
create: createRule$1({
|
|
977
|
+
messageId: "required",
|
|
978
|
+
data: { message: MESSAGE$1 }
|
|
979
|
+
})
|
|
980
|
+
};
|
|
981
|
+
//#endregion
|
|
571
982
|
//#region src/rules/outbox-listener-delivery-required.ts
|
|
572
983
|
const MESSAGE = "Outbox listeners should deliver posted activities explicitly with ctx.sendActivity() or ctx.forwardActivity().";
|
|
573
984
|
const isChainedFromOutboxListeners = (expr, federationTracker) => {
|
|
@@ -916,6 +1327,30 @@ Object.defineProperty(exports, "eslint$21", {
|
|
|
916
1327
|
return eslint$21;
|
|
917
1328
|
}
|
|
918
1329
|
});
|
|
1330
|
+
Object.defineProperty(exports, "eslint$22", {
|
|
1331
|
+
enumerable: true,
|
|
1332
|
+
get: function() {
|
|
1333
|
+
return eslint$22;
|
|
1334
|
+
}
|
|
1335
|
+
});
|
|
1336
|
+
Object.defineProperty(exports, "eslint$23", {
|
|
1337
|
+
enumerable: true,
|
|
1338
|
+
get: function() {
|
|
1339
|
+
return eslint$23;
|
|
1340
|
+
}
|
|
1341
|
+
});
|
|
1342
|
+
Object.defineProperty(exports, "eslint$24", {
|
|
1343
|
+
enumerable: true,
|
|
1344
|
+
get: function() {
|
|
1345
|
+
return eslint$24;
|
|
1346
|
+
}
|
|
1347
|
+
});
|
|
1348
|
+
Object.defineProperty(exports, "eslint$25", {
|
|
1349
|
+
enumerable: true,
|
|
1350
|
+
get: function() {
|
|
1351
|
+
return eslint$25;
|
|
1352
|
+
}
|
|
1353
|
+
});
|
|
919
1354
|
Object.defineProperty(exports, "eslint$3", {
|
|
920
1355
|
enumerable: true,
|
|
921
1356
|
get: function() {
|
package/dist/oxlint.cjs
CHANGED
|
@@ -1,28 +1,32 @@
|
|
|
1
|
-
const require_outbox_listener_delivery_required = require("./outbox-listener-delivery-required-
|
|
1
|
+
const require_outbox_listener_delivery_required = require("./outbox-listener-delivery-required-BhuVwj9X.cjs");
|
|
2
2
|
//#region src/oxlint.ts
|
|
3
3
|
const rules = {
|
|
4
|
-
[require_outbox_listener_delivery_required.RULE_IDS.actorIdMismatch]: require_outbox_listener_delivery_required.eslint$
|
|
5
|
-
[require_outbox_listener_delivery_required.RULE_IDS.actorIdRequired]: require_outbox_listener_delivery_required.eslint$
|
|
6
|
-
[require_outbox_listener_delivery_required.RULE_IDS.actorFollowingPropertyRequired]: require_outbox_listener_delivery_required.eslint$
|
|
7
|
-
[require_outbox_listener_delivery_required.RULE_IDS.actorFollowingPropertyMismatch]: require_outbox_listener_delivery_required.eslint$
|
|
8
|
-
[require_outbox_listener_delivery_required.RULE_IDS.actorFollowersPropertyRequired]: require_outbox_listener_delivery_required.eslint$
|
|
9
|
-
[require_outbox_listener_delivery_required.RULE_IDS.actorFollowersPropertyMismatch]: require_outbox_listener_delivery_required.eslint$
|
|
10
|
-
[require_outbox_listener_delivery_required.RULE_IDS.actorOutboxPropertyRequired]: require_outbox_listener_delivery_required.eslint$
|
|
11
|
-
[require_outbox_listener_delivery_required.RULE_IDS.actorOutboxPropertyMismatch]: require_outbox_listener_delivery_required.eslint$
|
|
12
|
-
[require_outbox_listener_delivery_required.RULE_IDS.actorLikedPropertyRequired]: require_outbox_listener_delivery_required.eslint$
|
|
13
|
-
[require_outbox_listener_delivery_required.RULE_IDS.actorLikedPropertyMismatch]: require_outbox_listener_delivery_required.eslint$
|
|
14
|
-
[require_outbox_listener_delivery_required.RULE_IDS.actorFeaturedPropertyRequired]: require_outbox_listener_delivery_required.eslint$
|
|
15
|
-
[require_outbox_listener_delivery_required.RULE_IDS.actorFeaturedPropertyMismatch]: require_outbox_listener_delivery_required.eslint$
|
|
16
|
-
[require_outbox_listener_delivery_required.RULE_IDS.actorFeaturedTagsPropertyRequired]: require_outbox_listener_delivery_required.eslint$
|
|
17
|
-
[require_outbox_listener_delivery_required.RULE_IDS.actorFeaturedTagsPropertyMismatch]: require_outbox_listener_delivery_required.eslint$
|
|
18
|
-
[require_outbox_listener_delivery_required.RULE_IDS.actorInboxPropertyRequired]: require_outbox_listener_delivery_required.eslint$
|
|
19
|
-
[require_outbox_listener_delivery_required.RULE_IDS.actorInboxPropertyMismatch]: require_outbox_listener_delivery_required.eslint$
|
|
20
|
-
[require_outbox_listener_delivery_required.RULE_IDS.actorSharedInboxPropertyRequired]: require_outbox_listener_delivery_required.eslint$
|
|
21
|
-
[require_outbox_listener_delivery_required.RULE_IDS.actorSharedInboxPropertyMismatch]: require_outbox_listener_delivery_required.eslint$
|
|
22
|
-
[require_outbox_listener_delivery_required.RULE_IDS.
|
|
23
|
-
[require_outbox_listener_delivery_required.RULE_IDS.
|
|
24
|
-
[require_outbox_listener_delivery_required.RULE_IDS.
|
|
25
|
-
[require_outbox_listener_delivery_required.RULE_IDS.
|
|
4
|
+
[require_outbox_listener_delivery_required.RULE_IDS.actorIdMismatch]: require_outbox_listener_delivery_required.eslint$16,
|
|
5
|
+
[require_outbox_listener_delivery_required.RULE_IDS.actorIdRequired]: require_outbox_listener_delivery_required.eslint$15,
|
|
6
|
+
[require_outbox_listener_delivery_required.RULE_IDS.actorFollowingPropertyRequired]: require_outbox_listener_delivery_required.eslint$17,
|
|
7
|
+
[require_outbox_listener_delivery_required.RULE_IDS.actorFollowingPropertyMismatch]: require_outbox_listener_delivery_required.eslint$18,
|
|
8
|
+
[require_outbox_listener_delivery_required.RULE_IDS.actorFollowersPropertyRequired]: require_outbox_listener_delivery_required.eslint$19,
|
|
9
|
+
[require_outbox_listener_delivery_required.RULE_IDS.actorFollowersPropertyMismatch]: require_outbox_listener_delivery_required.eslint$20,
|
|
10
|
+
[require_outbox_listener_delivery_required.RULE_IDS.actorOutboxPropertyRequired]: require_outbox_listener_delivery_required.eslint$9,
|
|
11
|
+
[require_outbox_listener_delivery_required.RULE_IDS.actorOutboxPropertyMismatch]: require_outbox_listener_delivery_required.eslint$10,
|
|
12
|
+
[require_outbox_listener_delivery_required.RULE_IDS.actorLikedPropertyRequired]: require_outbox_listener_delivery_required.eslint$11,
|
|
13
|
+
[require_outbox_listener_delivery_required.RULE_IDS.actorLikedPropertyMismatch]: require_outbox_listener_delivery_required.eslint$12,
|
|
14
|
+
[require_outbox_listener_delivery_required.RULE_IDS.actorFeaturedPropertyRequired]: require_outbox_listener_delivery_required.eslint$23,
|
|
15
|
+
[require_outbox_listener_delivery_required.RULE_IDS.actorFeaturedPropertyMismatch]: require_outbox_listener_delivery_required.eslint$24,
|
|
16
|
+
[require_outbox_listener_delivery_required.RULE_IDS.actorFeaturedTagsPropertyRequired]: require_outbox_listener_delivery_required.eslint$21,
|
|
17
|
+
[require_outbox_listener_delivery_required.RULE_IDS.actorFeaturedTagsPropertyMismatch]: require_outbox_listener_delivery_required.eslint$22,
|
|
18
|
+
[require_outbox_listener_delivery_required.RULE_IDS.actorInboxPropertyRequired]: require_outbox_listener_delivery_required.eslint$13,
|
|
19
|
+
[require_outbox_listener_delivery_required.RULE_IDS.actorInboxPropertyMismatch]: require_outbox_listener_delivery_required.eslint$14,
|
|
20
|
+
[require_outbox_listener_delivery_required.RULE_IDS.actorSharedInboxPropertyRequired]: require_outbox_listener_delivery_required.eslint$6,
|
|
21
|
+
[require_outbox_listener_delivery_required.RULE_IDS.actorSharedInboxPropertyMismatch]: require_outbox_listener_delivery_required.eslint$7,
|
|
22
|
+
[require_outbox_listener_delivery_required.RULE_IDS.actorUploadMediaPropertyRequired]: require_outbox_listener_delivery_required.eslint$4,
|
|
23
|
+
[require_outbox_listener_delivery_required.RULE_IDS.actorUploadMediaPropertyMismatch]: require_outbox_listener_delivery_required.eslint$5,
|
|
24
|
+
[require_outbox_listener_delivery_required.RULE_IDS.actorPublicKeyRequired]: require_outbox_listener_delivery_required.eslint$8,
|
|
25
|
+
[require_outbox_listener_delivery_required.RULE_IDS.actorAssertionMethodRequired]: require_outbox_listener_delivery_required.eslint$25,
|
|
26
|
+
[require_outbox_listener_delivery_required.RULE_IDS.collectionFilteringNotImplemented]: require_outbox_listener_delivery_required.eslint$3,
|
|
27
|
+
[require_outbox_listener_delivery_required.RULE_IDS.outboxListenerDeliveryRequired]: require_outbox_listener_delivery_required.eslint,
|
|
28
|
+
[require_outbox_listener_delivery_required.RULE_IDS.mediaUploaderObjectUriRequired]: require_outbox_listener_delivery_required.eslint$1,
|
|
29
|
+
[require_outbox_listener_delivery_required.RULE_IDS.mediaUploaderAuthorizationRequired]: require_outbox_listener_delivery_required.eslint$2
|
|
26
30
|
};
|
|
27
31
|
const plugin = {
|
|
28
32
|
meta: {
|
package/dist/oxlint.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { C as RULE_IDS, S as eslint$
|
|
1
|
+
import { C as eslint$13, D as RULE_IDS, E as eslint$21, O as name, S as eslint$12, T as eslint$11, _ as eslint, a as eslint$18, b as eslint$4, c as eslint$17, d as eslint$7, f as eslint$8, g as eslint$1, h as eslint$15, i as eslint$22, k as version, l as eslint$20, m as eslint$14, n as eslint$24, o as eslint$19, p as eslint$9, r as eslint$25, s as eslint$16, t as eslint$23, u as eslint$6, v as eslint$2, w as eslint$10, x as eslint$5, y as eslint$3 } from "./outbox-listener-delivery-required-BOIQaemq.js";
|
|
2
2
|
//#region src/oxlint.ts
|
|
3
3
|
const rules = {
|
|
4
4
|
[RULE_IDS.actorIdMismatch]: eslint,
|
|
@@ -19,10 +19,14 @@ const rules = {
|
|
|
19
19
|
[RULE_IDS.actorInboxPropertyMismatch]: eslint$15,
|
|
20
20
|
[RULE_IDS.actorSharedInboxPropertyRequired]: eslint$16,
|
|
21
21
|
[RULE_IDS.actorSharedInboxPropertyMismatch]: eslint$17,
|
|
22
|
-
[RULE_IDS.
|
|
23
|
-
[RULE_IDS.
|
|
24
|
-
[RULE_IDS.
|
|
25
|
-
[RULE_IDS.
|
|
22
|
+
[RULE_IDS.actorUploadMediaPropertyRequired]: eslint$18,
|
|
23
|
+
[RULE_IDS.actorUploadMediaPropertyMismatch]: eslint$19,
|
|
24
|
+
[RULE_IDS.actorPublicKeyRequired]: eslint$20,
|
|
25
|
+
[RULE_IDS.actorAssertionMethodRequired]: eslint$21,
|
|
26
|
+
[RULE_IDS.collectionFilteringNotImplemented]: eslint$22,
|
|
27
|
+
[RULE_IDS.outboxListenerDeliveryRequired]: eslint$23,
|
|
28
|
+
[RULE_IDS.mediaUploaderObjectUriRequired]: eslint$24,
|
|
29
|
+
[RULE_IDS.mediaUploaderAuthorizationRequired]: eslint$25
|
|
26
30
|
};
|
|
27
31
|
const plugin = {
|
|
28
32
|
meta: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fedify/lint",
|
|
3
|
-
"version": "2.4.0-dev.
|
|
3
|
+
"version": "2.4.0-dev.1634+3b4bfbb6",
|
|
4
4
|
"description": "Fedify linting rules and plugins",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"Fedify",
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
],
|
|
52
52
|
"peerDependencies": {
|
|
53
53
|
"eslint": ">=9.0.0",
|
|
54
|
-
"@fedify/fedify": "^2.4.0-dev.
|
|
54
|
+
"@fedify/fedify": "^2.4.0-dev.1634+3b4bfbb6"
|
|
55
55
|
},
|
|
56
56
|
"peerDependenciesMeta": {
|
|
57
57
|
"eslint": {
|