aws-iam-language-server 0.0.19 → 0.0.21
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/package.json +1 -1
- package/src/handlers/completion/principal-identifier-completions.js +6 -5
- package/src/handlers/completion/resource-value.js +3 -2
- package/src/handlers/diagnostics/resource.js +3 -2
- package/src/handlers/hover/principal-typed-value.js +3 -2
- package/src/lib/iam-policy/arn.d.ts +5 -0
- package/src/lib/iam-policy/arn.js +30 -1
package/package.json
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { CompletionItemKind, MarkupKind } from 'vscode-languageserver';
|
|
2
|
+
import { splitArn } from "../../lib/iam-policy/arn.js";
|
|
2
3
|
import { partitions } from "../../lib/iam-policy/partitions.js";
|
|
3
4
|
import { principalTypes } from "../../lib/iam-policy/principals.js";
|
|
4
5
|
import { partialRange } from "./index.js";
|
|
@@ -39,7 +40,7 @@ export function completePrincipalIdentifier(principalType, partial, position) {
|
|
|
39
40
|
}
|
|
40
41
|
return { items, isIncomplete: false };
|
|
41
42
|
}
|
|
42
|
-
const parts = partial
|
|
43
|
+
const parts = splitArn(partial);
|
|
43
44
|
if (parts.length === 1) {
|
|
44
45
|
if (config.arn.length > 0 && 'arn'.startsWith(partial.toLowerCase())) {
|
|
45
46
|
items.push({
|
|
@@ -70,7 +71,7 @@ export function completePrincipalIdentifier(principalType, partial, position) {
|
|
|
70
71
|
}
|
|
71
72
|
}
|
|
72
73
|
else if (parts.length === 3) {
|
|
73
|
-
const services = [...new Set(config.arn.map((pattern) => pattern
|
|
74
|
+
const services = [...new Set(config.arn.map((pattern) => splitArn(pattern)[2]))];
|
|
74
75
|
for (const service of services) {
|
|
75
76
|
if (`${parts[0]}:${parts[1]}:${service}`.toLowerCase().startsWith(partial.toLowerCase())) {
|
|
76
77
|
items.push({ label: service, kind: CompletionItemKind.Enum });
|
|
@@ -79,8 +80,8 @@ export function completePrincipalIdentifier(principalType, partial, position) {
|
|
|
79
80
|
}
|
|
80
81
|
else if (parts.length === 4) {
|
|
81
82
|
const service = parts[2];
|
|
82
|
-
const serviceArns = config.arn.filter((pattern) => pattern
|
|
83
|
-
const hasRegionArn = serviceArns.some((pattern) => pattern
|
|
83
|
+
const serviceArns = config.arn.filter((pattern) => splitArn(pattern)[2] === service);
|
|
84
|
+
const hasRegionArn = serviceArns.some((pattern) => splitArn(pattern)[3].length > 0);
|
|
84
85
|
if (!hasRegionArn) {
|
|
85
86
|
items.push({
|
|
86
87
|
label: ':',
|
|
@@ -118,7 +119,7 @@ export function completePrincipalIdentifier(principalType, partial, position) {
|
|
|
118
119
|
const region = parts[3];
|
|
119
120
|
const account = parts[4];
|
|
120
121
|
const matching = config.arn.filter((pattern) => {
|
|
121
|
-
const patternParts = pattern
|
|
122
|
+
const patternParts = splitArn(pattern);
|
|
122
123
|
if (patternParts[2] !== service)
|
|
123
124
|
return false;
|
|
124
125
|
if (region.length > 0 !== patternParts[3].length > 0)
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { CompletionItemKind, MarkupKind } from 'vscode-languageserver';
|
|
2
|
+
import { splitArn } from "../../lib/iam-policy/arn.js";
|
|
2
3
|
import { partitions } from "../../lib/iam-policy/partitions.js";
|
|
3
4
|
import { formatResourceDocumentation } from "../../lib/iam-policy/reference/documentation.js";
|
|
4
5
|
import { ServiceReference } from "../../lib/iam-policy/reference/services.js";
|
|
5
6
|
import { expandActionPattern } from "../../lib/iam-policy/wildcard.js";
|
|
6
7
|
import { partialRange } from "./index.js";
|
|
7
8
|
export function completeResourceValue(location, context) {
|
|
8
|
-
const parts = location.partial
|
|
9
|
+
const parts = splitArn(location.partial);
|
|
9
10
|
const range = partialRange(context.position, location.partial.length);
|
|
10
11
|
const items = [];
|
|
11
12
|
const statement = context.handler.getStatementContext(context.uri, context.position);
|
|
@@ -127,7 +128,7 @@ export function completeResourceValue(location, context) {
|
|
|
127
128
|
const resources = ServiceReference.getResourcesForActions(expandActionPattern(`${service}:*`));
|
|
128
129
|
for (const resource of resources) {
|
|
129
130
|
for (const arn of resource.arnFormats) {
|
|
130
|
-
const patternParts = arn
|
|
131
|
+
const patternParts = splitArn(arn);
|
|
131
132
|
const patternRegion = patternParts[3];
|
|
132
133
|
const patternAccount = patternParts[4];
|
|
133
134
|
if (region.length > 0 !== patternRegion.length > 0)
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { isRuleEnabled } from "../../lib/config.js";
|
|
2
|
+
import { splitArn } from "../../lib/iam-policy/arn.js";
|
|
2
3
|
import { isRegionValidForPartition, isValidPartition, partitions } from "../../lib/iam-policy/partitions.js";
|
|
3
4
|
import { ElementValidator } from "./base.js";
|
|
4
5
|
import { createDiagnostic } from "./utils.js";
|
|
5
6
|
const validPartitions = Object.keys(partitions);
|
|
6
7
|
const accountIdPattern = /^\d{12}$/;
|
|
7
8
|
function segmentRange(value, segmentIndex) {
|
|
8
|
-
const segments = value.text
|
|
9
|
+
const segments = splitArn(value.text);
|
|
9
10
|
let offset = 0;
|
|
10
11
|
for (let i = 0; i < segmentIndex; i++) {
|
|
11
12
|
offset += segments[i].length + 1;
|
|
@@ -22,7 +23,7 @@ function validateArn(value) {
|
|
|
22
23
|
const diagnostics = [];
|
|
23
24
|
if (!text.startsWith('arn:'))
|
|
24
25
|
return [];
|
|
25
|
-
const segments = text
|
|
26
|
+
const segments = splitArn(text);
|
|
26
27
|
if (segments.length > 1 && isRuleEnabled('INVALID_PARTITION')) {
|
|
27
28
|
const partition = segments[1];
|
|
28
29
|
if (partition === '') {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { MarkupKind } from 'vscode-languageserver';
|
|
2
|
+
import { splitArn } from "../../lib/iam-policy/arn.js";
|
|
2
3
|
import { formatPrincipalTypedValueDocumentation, principalTypedValues, } from "../../lib/iam-policy/reference/documentation.js";
|
|
3
4
|
import { ServiceReference } from "../../lib/iam-policy/reference/services.js";
|
|
4
5
|
export function handlePrincipalTypedValueHover(location) {
|
|
@@ -35,7 +36,7 @@ export function handlePrincipalTypedValueHover(location) {
|
|
|
35
36
|
};
|
|
36
37
|
}
|
|
37
38
|
if (location.value.startsWith('arn:')) {
|
|
38
|
-
const parts = location.value
|
|
39
|
+
const parts = splitArn(location.value);
|
|
39
40
|
const resource = parts.slice(5).join(':');
|
|
40
41
|
if (resource.startsWith('root')) {
|
|
41
42
|
return {
|
|
@@ -77,7 +78,7 @@ export function handlePrincipalTypedValueHover(location) {
|
|
|
77
78
|
}
|
|
78
79
|
if (principalType === 'Federated') {
|
|
79
80
|
if (location.value.startsWith('arn:')) {
|
|
80
|
-
const resource = location.value
|
|
81
|
+
const resource = splitArn(location.value).slice(5).join(':');
|
|
81
82
|
if (resource.startsWith('oidc-provider/')) {
|
|
82
83
|
return {
|
|
83
84
|
range: location.range,
|
|
@@ -5,6 +5,11 @@ export type ArnParts = {
|
|
|
5
5
|
account: string;
|
|
6
6
|
resource: string;
|
|
7
7
|
};
|
|
8
|
+
/**
|
|
9
|
+
* Split a string on `:` while preserving `${...}` placeholders intact.
|
|
10
|
+
* Colons inside `${...}` (e.g. `${AWS::AccountId}`) are not treated as delimiters.
|
|
11
|
+
*/
|
|
12
|
+
export declare function splitArn(arn: string): string[];
|
|
8
13
|
/**
|
|
9
14
|
* Parse an ARN string into its structural components.
|
|
10
15
|
* Returns null if the string is not a valid ARN structure (fewer than 6 colon-separated segments).
|
|
@@ -1,10 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Split a string on `:` while preserving `${...}` placeholders intact.
|
|
3
|
+
* Colons inside `${...}` (e.g. `${AWS::AccountId}`) are not treated as delimiters.
|
|
4
|
+
*/
|
|
5
|
+
export function splitArn(arn) {
|
|
6
|
+
const segments = [];
|
|
7
|
+
let current = '';
|
|
8
|
+
let depth = 0;
|
|
9
|
+
for (let i = 0; i < arn.length; i++) {
|
|
10
|
+
if (arn[i] === '$' && arn[i + 1] === '{') {
|
|
11
|
+
depth++;
|
|
12
|
+
current += '${';
|
|
13
|
+
i++;
|
|
14
|
+
}
|
|
15
|
+
else if (depth > 0 && arn[i] === '}') {
|
|
16
|
+
depth--;
|
|
17
|
+
current += '}';
|
|
18
|
+
}
|
|
19
|
+
else if (depth === 0 && arn[i] === ':') {
|
|
20
|
+
segments.push(current);
|
|
21
|
+
current = '';
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
current += arn[i];
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
segments.push(current);
|
|
28
|
+
return segments;
|
|
29
|
+
}
|
|
1
30
|
/**
|
|
2
31
|
* Parse an ARN string into its structural components.
|
|
3
32
|
* Returns null if the string is not a valid ARN structure (fewer than 6 colon-separated segments).
|
|
4
33
|
* Everything after the 5th colon is treated as the resource portion.
|
|
5
34
|
*/
|
|
6
35
|
export function parseArn(arn) {
|
|
7
|
-
const segments = arn
|
|
36
|
+
const segments = splitArn(arn);
|
|
8
37
|
if (segments.length < 6)
|
|
9
38
|
return null;
|
|
10
39
|
if (segments[0] !== 'arn')
|