aws-iam-language-server 0.0.13 → 0.0.14

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.
Files changed (51) hide show
  1. package/package.json +1 -1
  2. package/readme.md +11 -0
  3. package/src/handlers/completion/action-value.d.ts +2 -0
  4. package/src/handlers/completion/action-value.js +1 -1
  5. package/src/handlers/completion/condition-key.d.ts +2 -0
  6. package/src/handlers/completion/condition-key.js +3 -3
  7. package/src/handlers/completion/index.d.ts +2 -2
  8. package/src/handlers/completion/index.js +4 -4
  9. package/src/handlers/completion/principal-identifier-completions.d.ts +1 -1
  10. package/src/handlers/diagnostics/resource.js +4 -4
  11. package/src/handlers/diagnostics/utils.js +1 -13
  12. package/src/handlers/document-link/document-link.js +1 -3
  13. package/src/handlers/hover/action-value.d.ts +3 -0
  14. package/src/handlers/hover/action-value.js +21 -0
  15. package/src/handlers/hover/condition-block.d.ts +3 -0
  16. package/src/handlers/hover/condition-block.js +18 -0
  17. package/src/handlers/hover/condition-key.d.ts +3 -0
  18. package/src/handlers/hover/condition-key.js +53 -0
  19. package/src/handlers/hover/condition-operator.d.ts +3 -0
  20. package/src/handlers/hover/condition-operator.js +14 -0
  21. package/src/handlers/hover/effect-value.d.ts +3 -0
  22. package/src/handlers/hover/effect-value.js +17 -0
  23. package/src/handlers/hover/index.d.ts +3 -0
  24. package/src/handlers/hover/index.js +69 -0
  25. package/src/handlers/hover/principal-block.d.ts +3 -0
  26. package/src/handlers/hover/principal-block.js +17 -0
  27. package/src/handlers/hover/principal-type.d.ts +3 -0
  28. package/src/handlers/hover/principal-type.js +25 -0
  29. package/src/handlers/hover/principal-typed-value.d.ts +3 -0
  30. package/src/handlers/hover/principal-typed-value.js +118 -0
  31. package/src/handlers/hover/principal-value.d.ts +3 -0
  32. package/src/handlers/hover/principal-value.js +13 -0
  33. package/src/handlers/hover/resource-value.d.ts +3 -0
  34. package/src/handlers/hover/resource-value.js +45 -0
  35. package/src/handlers/hover/statement-block.d.ts +3 -0
  36. package/src/handlers/hover/statement-block.js +14 -0
  37. package/src/handlers/hover/statement-key.d.ts +3 -0
  38. package/src/handlers/hover/statement-key.js +14 -0
  39. package/src/lib/iam-policy/arn.d.ts +17 -0
  40. package/src/lib/iam-policy/arn.js +77 -0
  41. package/src/lib/iam-policy/location.d.ts +31 -1
  42. package/src/lib/iam-policy/location.js +56 -19
  43. package/src/lib/iam-policy/reference/services.d.ts +5 -3
  44. package/src/lib/iam-policy/reference/services.js +58 -19
  45. package/src/lib/iam-policy/reference/types.d.ts +5 -0
  46. package/src/lib/treesitter/base.d.ts +3 -8
  47. package/src/lib/treesitter/base.js +3 -3
  48. package/src/lib/treesitter/hcl.js +32 -24
  49. package/src/lib/treesitter/json.js +14 -11
  50. package/src/lib/treesitter/yaml.js +30 -27
  51. package/src/server.js +5 -3
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aws-iam-language-server",
3
- "version": "0.0.13",
3
+ "version": "0.0.14",
4
4
  "type": "module",
5
5
  "bin": "./src/server.js",
6
6
  "publisher": "MichaelBarney",
package/readme.md CHANGED
@@ -59,6 +59,17 @@ This language server provides completion on:
59
59
  - condition operators (`StringLike`, `ForAnyValue:*`, etc)
60
60
  - condition keys (global keys like `aws:RequestTag/${TagKey}`, action-specific keys like `s3:TlsVersion`)
61
61
 
62
+ ### Hover
63
+
64
+ Hovering over elements within a policy document will show contextual documentation:
65
+
66
+ - actions (access level, resource types, condition keys, and dependent actions)
67
+ - resources (matched resource type from the service reference with ARN format and condition keys)
68
+ - principal types (description of `AWS`, `Service`, `Federated`, `CanonicalUser`)
69
+ - principal values (identifies account IDs, role/user ARNs, service principals, federated providers)
70
+ - condition operators (description of what each operator does, like `StringEquals`, `ArnLike`, `IpAddress`, etc.)
71
+ - condition keys (documentation for global keys like `aws:SourceIp` and service-specific keys like `s3:prefix`)
72
+
62
73
  ### Diagnostics
63
74
 
64
75
  This language server will provide diagnostics for some IAM policy issues, including:
@@ -1,4 +1,6 @@
1
1
  import type { CompletionList } from 'vscode-languageserver';
2
2
  import type { ActionValueLocation } from '../../lib/iam-policy/location.ts';
3
+ import type { Action } from '../../lib/iam-policy/reference/types.ts';
3
4
  import { type CompletionContext } from './index.ts';
4
5
  export declare function completeActionValue(location: ActionValueLocation, context: CompletionContext): CompletionList;
6
+ export declare function formatActionDocumentation(action: Action): string;
@@ -44,7 +44,7 @@ export function completeActionValue(location, context) {
44
44
  }
45
45
  return { items, isIncomplete: false };
46
46
  }
47
- function formatActionDocumentation(action) {
47
+ export function formatActionDocumentation(action) {
48
48
  const parts = [];
49
49
  if (action.accessLevel)
50
50
  parts.push(`**Access Level**: ${action.accessLevel}`);
@@ -1,4 +1,6 @@
1
1
  import type { CompletionList } from 'vscode-languageserver';
2
2
  import type { ConditionKeyLocation } from '../../lib/iam-policy/location.ts';
3
+ import type { ConditionKey, GlobalConditionKey } from '../../lib/iam-policy/reference/types.ts';
3
4
  import { type CompletionContext } from './index.ts';
4
5
  export declare function completeConditionKey(location: ConditionKeyLocation, context: CompletionContext): CompletionList;
6
+ export declare function formatConditionKeyDocumentation(types: string[], global: GlobalConditionKey | undefined, conditionKey?: ConditionKey): string;
@@ -39,7 +39,7 @@ export function completeConditionKey(location, context) {
39
39
  textEdit: { range, newText: key.name },
40
40
  documentation: {
41
41
  kind: MarkupKind.Markdown,
42
- value: formatDocumentation(key.types, global, conditionKeyData),
42
+ value: formatConditionKeyDocumentation(key.types, global, conditionKeyData),
43
43
  },
44
44
  });
45
45
  }
@@ -59,13 +59,13 @@ export function completeConditionKey(location, context) {
59
59
  textEdit: { range, newText: global.name },
60
60
  documentation: {
61
61
  kind: MarkupKind.Markdown,
62
- value: formatDocumentation([], global),
62
+ value: formatConditionKeyDocumentation([], global),
63
63
  },
64
64
  });
65
65
  }
66
66
  return { items, isIncomplete: false };
67
67
  }
68
- function formatDocumentation(types, global, conditionKey) {
68
+ export function formatConditionKeyDocumentation(types, global, conditionKey) {
69
69
  const parts = [];
70
70
  const description = global?.description ?? conditionKey?.description;
71
71
  if (description)
@@ -8,12 +8,12 @@ export type CompletionContext = {
8
8
  uri: string;
9
9
  position: {
10
10
  line: number;
11
- column: number;
11
+ character: number;
12
12
  };
13
13
  };
14
14
  export declare function partialRange(position: {
15
15
  line: number;
16
- column: number;
16
+ character: number;
17
17
  }, partialLength: number): {
18
18
  start: {
19
19
  line: number;
@@ -17,15 +17,15 @@ import { completeStatementKey } from "./statement-key.js";
17
17
  const emptyResult = { items: [], isIncomplete: false };
18
18
  export function partialRange(position, partialLength) {
19
19
  return {
20
- start: { line: position.line, character: position.column - partialLength },
21
- end: { line: position.line, character: position.column },
20
+ start: { line: position.line, character: position.character - partialLength },
21
+ end: { line: position.line, character: position.character },
22
22
  };
23
23
  }
24
24
  export async function handleCompletionRequest(params, _documents, treeManager, connection) {
25
25
  const handler = treeManager.getLanguageHandler(params.textDocument.uri);
26
26
  if (!handler)
27
27
  return emptyResult;
28
- const position = { line: params.position.line, column: params.position.character };
28
+ const position = params.position;
29
29
  const cursorContext = handler.getCursorContext(params.textDocument.uri, position);
30
30
  if (!cursorContext)
31
31
  return emptyResult;
@@ -42,7 +42,7 @@ export async function handleCompletionRequest(params, _documents, treeManager, c
42
42
  position,
43
43
  });
44
44
  result.items = result.items.map(toSnippet);
45
- connection.console.debug(`Found ${result.items.length} completion items for ${params.textDocument.uri} at line ${position.line}, column ${position.column}`);
45
+ connection.console.debug(`Found ${result.items.length} completion items for ${params.textDocument.uri} at line ${position.line}, character ${position.character}`);
46
46
  return result;
47
47
  }
48
48
  function handleLocationCompletion(location, context) {
@@ -1,5 +1,5 @@
1
1
  import type { CompletionList } from 'vscode-languageserver';
2
2
  export declare function completePrincipalIdentifier(principalType: string | null, partial: string, position: {
3
3
  line: number;
4
- column: number;
4
+ character: number;
5
5
  }): CompletionList;
@@ -9,11 +9,11 @@ function segmentRange(value, segmentIndex) {
9
9
  for (let i = 0; i < segmentIndex; i++) {
10
10
  offset += segments[i].length + 1;
11
11
  }
12
- const startColumn = value.range.start.column + offset;
13
- const endColumn = startColumn + segments[segmentIndex].length;
12
+ const startCharacter = value.range.start.character + offset;
13
+ const endCharacter = startCharacter + segments[segmentIndex].length;
14
14
  return {
15
- start: { line: value.range.start.line, column: startColumn },
16
- end: { line: value.range.start.line, column: endColumn },
15
+ start: { line: value.range.start.line, character: startCharacter },
16
+ end: { line: value.range.start.line, character: endCharacter },
17
17
  };
18
18
  }
19
19
  function validateArn(value) {
@@ -2,18 +2,6 @@ export function createDiagnostic(message, range) {
2
2
  return {
3
3
  source: 'aws-iam-language-server',
4
4
  message,
5
- range: convertRangeToDiagnosticRange(range),
6
- };
7
- }
8
- function convertRangeToDiagnosticRange(range) {
9
- return {
10
- start: {
11
- character: range.start.column,
12
- line: range.start.line,
13
- },
14
- end: {
15
- character: range.end.column,
16
- line: range.end.line,
17
- },
5
+ range,
18
6
  };
19
7
  }
@@ -21,9 +21,7 @@ export function documentLinkHandler(params, documents, treeManager, connection)
21
21
  const action = ServiceReference.getAction(value.text);
22
22
  if (!action)
23
23
  continue;
24
- const start = { line: value.range.start.line, character: value.range.start.column };
25
- const end = { line: value.range.end.line, character: value.range.end.column };
26
- const range = { start, end };
24
+ const range = value.range;
27
25
  if (action.iamUrl) {
28
26
  links.push({
29
27
  range,
@@ -0,0 +1,3 @@
1
+ import { type Hover } from 'vscode-languageserver';
2
+ import type { ActionValueLocation } from '../../lib/iam-policy/location.ts';
3
+ export declare function handleActionValueHover(location: ActionValueLocation): Hover | null;
@@ -0,0 +1,21 @@
1
+ import { MarkupKind } from 'vscode-languageserver';
2
+ import { ServiceReference } from "../../lib/iam-policy/reference/services.js";
3
+ import { formatActionDocumentation } from "../completion/action-value.js";
4
+ export function handleActionValueHover(location) {
5
+ const action = ServiceReference.getAction(location.value);
6
+ if (!action)
7
+ return null;
8
+ const lines = [`**${action.service}:${action.name}**`];
9
+ if (action.description)
10
+ lines.push(action.description);
11
+ const docs = formatActionDocumentation(action);
12
+ if (docs)
13
+ lines.push(docs);
14
+ return {
15
+ range: location.range,
16
+ contents: {
17
+ kind: MarkupKind.Markdown,
18
+ value: lines.join('\n\n'),
19
+ },
20
+ };
21
+ }
@@ -0,0 +1,3 @@
1
+ import { type Hover } from 'vscode-languageserver';
2
+ import type { ConditionBlockLocation } from '../../lib/iam-policy/location.ts';
3
+ export declare function handleConditionBlockHover(location: ConditionBlockLocation): Hover | null;
@@ -0,0 +1,18 @@
1
+ import { MarkupKind } from 'vscode-languageserver';
2
+ const conditionBlockAttributes = {
3
+ test: 'The condition operator to evaluate (e.g., `StringEquals`, `ArnLike`, `IpAddress`).',
4
+ variable: 'The condition key to evaluate (e.g., `aws:SourceIp`, `s3:prefix`).',
5
+ values: 'List of values to compare against the condition key.',
6
+ };
7
+ export function handleConditionBlockHover(location) {
8
+ const description = conditionBlockAttributes[location.value];
9
+ if (!description)
10
+ return null;
11
+ return {
12
+ range: location.range,
13
+ contents: {
14
+ kind: MarkupKind.Markdown,
15
+ value: description,
16
+ },
17
+ };
18
+ }
@@ -0,0 +1,3 @@
1
+ import { type Hover } from 'vscode-languageserver';
2
+ import type { ConditionKeyLocation } from '../../lib/iam-policy/location.ts';
3
+ export declare function handleConditionKeyHover(location: ConditionKeyLocation): Hover | null;
@@ -0,0 +1,53 @@
1
+ import { MarkupKind } from 'vscode-languageserver';
2
+ import { ServiceReference } from "../../lib/iam-policy/reference/services.js";
3
+ import { formatConditionKeyDocumentation } from "../completion/condition-key.js";
4
+ const placeholderPattern = /\$\{[^}]+\}/g;
5
+ function patternToRegex(pattern) {
6
+ const escaped = pattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
7
+ const withPlaceholders = escaped.replace(/\\\$\\\{[^}]+\\\}/g, '.+');
8
+ return new RegExp(`^${withPlaceholders}$`);
9
+ }
10
+ function findByPattern(keyName, globals, service) {
11
+ for (const global of globals) {
12
+ if (placeholderPattern.test(global.name) && patternToRegex(global.name).test(keyName)) {
13
+ const conditionKey = service ? ServiceReference.getConditionKey(service, global.name) : undefined;
14
+ return { global, conditionKey };
15
+ }
16
+ }
17
+ if (service) {
18
+ const serviceData = ServiceReference.getServiceData(service);
19
+ if (serviceData) {
20
+ for (const name of Object.keys(serviceData.conditionKeys)) {
21
+ if (placeholderPattern.test(name) && patternToRegex(name).test(keyName)) {
22
+ return { conditionKey: serviceData.conditionKeys[name] };
23
+ }
24
+ }
25
+ }
26
+ }
27
+ return null;
28
+ }
29
+ export function handleConditionKeyHover(location) {
30
+ const keyName = location.value;
31
+ if (!keyName)
32
+ return null;
33
+ const globalKeys = ServiceReference.getGlobalConditionKeys();
34
+ const service = keyName.split(':')[0];
35
+ let global = globalKeys.find((k) => k.name === keyName);
36
+ let conditionKey = service ? ServiceReference.getConditionKey(service, keyName) : undefined;
37
+ if (!global && !conditionKey) {
38
+ const match = findByPattern(keyName, globalKeys, service);
39
+ if (!match)
40
+ return null;
41
+ global = match.global;
42
+ conditionKey = match.conditionKey;
43
+ }
44
+ const types = conditionKey?.types ?? [];
45
+ const docs = formatConditionKeyDocumentation(types, global, conditionKey);
46
+ return {
47
+ range: location.range,
48
+ contents: {
49
+ kind: MarkupKind.Markdown,
50
+ value: `**${keyName}**\n\n${docs}`,
51
+ },
52
+ };
53
+ }
@@ -0,0 +1,3 @@
1
+ import { type Hover } from 'vscode-languageserver';
2
+ import type { ConditionOperatorLocation } from '../../lib/iam-policy/location.ts';
3
+ export declare function handleConditionOperatorHover(location: ConditionOperatorLocation): Hover | null;
@@ -0,0 +1,14 @@
1
+ import { MarkupKind } from 'vscode-languageserver';
2
+ import { conditionOperators } from "../../lib/iam-policy/condition-operators.js";
3
+ export function handleConditionOperatorHover(location) {
4
+ const operator = conditionOperators[location.value];
5
+ if (!operator)
6
+ return null;
7
+ return {
8
+ range: location.range,
9
+ contents: {
10
+ kind: MarkupKind.Markdown,
11
+ value: operator.description,
12
+ },
13
+ };
14
+ }
@@ -0,0 +1,3 @@
1
+ import { type Hover } from 'vscode-languageserver';
2
+ import type { EffectValueLocation } from '../../lib/iam-policy/location.ts';
3
+ export declare function handleEffectValueHover(location: EffectValueLocation): Hover | null;
@@ -0,0 +1,17 @@
1
+ import { MarkupKind } from 'vscode-languageserver';
2
+ const effectDescriptions = {
3
+ Allow: 'Grants access to the specified resources and actions.\n\nBy default, all requests are implicitly denied. An `Allow` overrides implicit denies but never overrides an explicit `Deny`.',
4
+ Deny: 'Explicitly denies access to the specified resources and actions.\n\nAn explicit `Deny` always takes precedence — it overrides any `Allow` statements, regardless of where they appear.',
5
+ };
6
+ export function handleEffectValueHover(location) {
7
+ const description = effectDescriptions[location.value];
8
+ if (!description)
9
+ return null;
10
+ return {
11
+ range: location.range,
12
+ contents: {
13
+ kind: MarkupKind.Markdown,
14
+ value: description,
15
+ },
16
+ };
17
+ }
@@ -0,0 +1,3 @@
1
+ import type { Connection, Hover, HoverParams } from 'vscode-languageserver';
2
+ import type { TreeManager } from '../../lib/treesitter/manager.ts';
3
+ export declare function hoverHandler(params: HoverParams, treeManager: TreeManager, connection: Connection): Hover | null;
@@ -0,0 +1,69 @@
1
+ import { resolvePolicyLocation } from "../../lib/iam-policy/location.js";
2
+ import { handleActionValueHover } from "./action-value.js";
3
+ import { handleConditionBlockHover } from "./condition-block.js";
4
+ import { handleConditionKeyHover } from "./condition-key.js";
5
+ import { handleConditionOperatorHover } from "./condition-operator.js";
6
+ import { handleEffectValueHover } from "./effect-value.js";
7
+ import { handlePrincipalBlockHover } from "./principal-block.js";
8
+ import { handlePrincipalTypeHover } from "./principal-type.js";
9
+ import { handlePrincipalTypedValueHover } from "./principal-typed-value.js";
10
+ import { handlePrincipalValueHover } from "./principal-value.js";
11
+ import { handleResourceValueHover } from "./resource-value.js";
12
+ import { handleStatementBlockHover } from "./statement-block.js";
13
+ import { handleStatementKeyHover } from "./statement-key.js";
14
+ export function hoverHandler(params, treeManager, connection) {
15
+ const handler = treeManager.getLanguageHandler(params.textDocument.uri);
16
+ if (!handler)
17
+ return null;
18
+ const position = params.position;
19
+ const cursorContext = handler.getCursorContext(params.textDocument.uri, position);
20
+ if (!cursorContext)
21
+ return null;
22
+ const location = resolvePolicyLocation(cursorContext);
23
+ connection.console.debug(`Hover debug: ${JSON.stringify({
24
+ uri: params.textDocument.uri,
25
+ cursorContext,
26
+ location,
27
+ position: params.position,
28
+ })}`);
29
+ return handleLocationHover(connection, location);
30
+ }
31
+ function handleLocationHover(connection, location) {
32
+ if (location.type === 'statement-key') {
33
+ return handleStatementKeyHover(connection, location);
34
+ }
35
+ else if (location.type === 'statement-block') {
36
+ return handleStatementBlockHover(location);
37
+ }
38
+ else if (location.type === 'effect-value') {
39
+ return handleEffectValueHover(location);
40
+ }
41
+ else if (location.type === 'action-value') {
42
+ return handleActionValueHover(location);
43
+ }
44
+ else if (location.type === 'resource-value') {
45
+ return handleResourceValueHover(location);
46
+ }
47
+ else if (location.type === 'principal-value') {
48
+ return handlePrincipalValueHover(location);
49
+ }
50
+ else if (location.type === 'principal-type' || location.type === 'principal-block-type') {
51
+ return handlePrincipalTypeHover(location);
52
+ }
53
+ else if (location.type === 'principal-block') {
54
+ return handlePrincipalBlockHover(location);
55
+ }
56
+ else if (location.type === 'principal-typed-value' || location.type === 'principal-block-identifier') {
57
+ return handlePrincipalTypedValueHover(location);
58
+ }
59
+ else if (location.type === 'condition-block') {
60
+ return handleConditionBlockHover(location);
61
+ }
62
+ else if (location.type === 'condition-operator') {
63
+ return handleConditionOperatorHover(location);
64
+ }
65
+ else if (location.type === 'condition-key') {
66
+ return handleConditionKeyHover(location);
67
+ }
68
+ return null;
69
+ }
@@ -0,0 +1,3 @@
1
+ import { type Hover } from 'vscode-languageserver';
2
+ import type { PrincipalBlockLocation } from '../../lib/iam-policy/location.ts';
3
+ export declare function handlePrincipalBlockHover(location: PrincipalBlockLocation): Hover | null;
@@ -0,0 +1,17 @@
1
+ import { MarkupKind } from 'vscode-languageserver';
2
+ const principalBlockAttributes = {
3
+ type: 'The type of principal. Valid values: `*`, `AWS`, `Service`, `Federated`, `CanonicalUser`.',
4
+ identifiers: 'List of principal identifiers. The format depends on the `type` attribute.',
5
+ };
6
+ export function handlePrincipalBlockHover(location) {
7
+ const description = principalBlockAttributes[location.value];
8
+ if (!description)
9
+ return null;
10
+ return {
11
+ range: location.range,
12
+ contents: {
13
+ kind: MarkupKind.Markdown,
14
+ value: description,
15
+ },
16
+ };
17
+ }
@@ -0,0 +1,3 @@
1
+ import { type Hover } from 'vscode-languageserver';
2
+ import type { PrincipalBlockTypeLocation, PrincipalTypeLocation } from '../../lib/iam-policy/location.ts';
3
+ export declare function handlePrincipalTypeHover(location: PrincipalTypeLocation | PrincipalBlockTypeLocation): Hover | null;
@@ -0,0 +1,25 @@
1
+ import { MarkupKind } from 'vscode-languageserver';
2
+ import { principalTypes } from "../../lib/iam-policy/principals.js";
3
+ export function handlePrincipalTypeHover(location) {
4
+ if (location.value === '*') {
5
+ return {
6
+ range: location.range,
7
+ contents: {
8
+ kind: MarkupKind.Markdown,
9
+ value: '**Public (unauthenticated) access**\n\nMatches all principals, including anonymous users.',
10
+ },
11
+ };
12
+ }
13
+ for (const principalType of Object.values(principalTypes)) {
14
+ if (principalType.value === location.value) {
15
+ return {
16
+ range: location.range,
17
+ contents: {
18
+ kind: MarkupKind.Markdown,
19
+ value: principalType.description,
20
+ },
21
+ };
22
+ }
23
+ }
24
+ return null;
25
+ }
@@ -0,0 +1,3 @@
1
+ import { type Hover } from 'vscode-languageserver';
2
+ import type { PrincipalBlockIdentifierLocation, PrincipalTypedValueLocation } from '../../lib/iam-policy/location.ts';
3
+ export declare function handlePrincipalTypedValueHover(location: PrincipalTypedValueLocation | PrincipalBlockIdentifierLocation): Hover | null;
@@ -0,0 +1,118 @@
1
+ import { MarkupKind } from 'vscode-languageserver';
2
+ import { ServiceReference } from "../../lib/iam-policy/reference/services.js";
3
+ export function handlePrincipalTypedValueHover(location) {
4
+ if (location.value === '*') {
5
+ return {
6
+ range: location.range,
7
+ contents: {
8
+ kind: MarkupKind.Markdown,
9
+ value: '**All principals** of this type.',
10
+ },
11
+ };
12
+ }
13
+ const principalType = location.principalType;
14
+ if (principalType === 'Service') {
15
+ const principals = ServiceReference.getServicePrincipals();
16
+ if (principals.includes(location.value)) {
17
+ const serviceName = location.value.split('.')[0];
18
+ return {
19
+ range: location.range,
20
+ contents: {
21
+ kind: MarkupKind.Markdown,
22
+ value: `**AWS service principal**\n\n\`${location.value}\` — allows the \`${serviceName}\` service to assume this role or access this resource.`,
23
+ },
24
+ };
25
+ }
26
+ }
27
+ if (principalType === 'AWS') {
28
+ if (/^\d{12}$/.test(location.value)) {
29
+ return {
30
+ range: location.range,
31
+ contents: {
32
+ kind: MarkupKind.Markdown,
33
+ value: `**AWS account**\n\nGrants access to the root user and all IAM identities in account \`${location.value}\`.`,
34
+ },
35
+ };
36
+ }
37
+ if (location.value.startsWith('arn:')) {
38
+ const parts = location.value.split(':');
39
+ const resource = parts.slice(5).join(':');
40
+ if (resource.startsWith('root')) {
41
+ return {
42
+ range: location.range,
43
+ contents: {
44
+ kind: MarkupKind.Markdown,
45
+ value: `**AWS account root user**\n\nGrants access to the root user of this account. Equivalent to specifying the account ID.`,
46
+ },
47
+ };
48
+ }
49
+ if (resource.startsWith('role/')) {
50
+ return {
51
+ range: location.range,
52
+ contents: {
53
+ kind: MarkupKind.Markdown,
54
+ value: `**IAM role**\n\n\`${resource}\`\n\nAny identity that assumes this role will have the permissions granted by this statement.`,
55
+ },
56
+ };
57
+ }
58
+ if (resource.startsWith('user/')) {
59
+ return {
60
+ range: location.range,
61
+ contents: {
62
+ kind: MarkupKind.Markdown,
63
+ value: `**IAM user**\n\n\`${resource}\``,
64
+ },
65
+ };
66
+ }
67
+ if (resource.startsWith('assumed-role/')) {
68
+ return {
69
+ range: location.range,
70
+ contents: {
71
+ kind: MarkupKind.Markdown,
72
+ value: `**Assumed role session**\n\n\`${resource}\``,
73
+ },
74
+ };
75
+ }
76
+ }
77
+ }
78
+ if (principalType === 'Federated') {
79
+ if (location.value.startsWith('arn:')) {
80
+ const resource = location.value.split(':').slice(5).join(':');
81
+ if (resource.startsWith('oidc-provider/')) {
82
+ return {
83
+ range: location.range,
84
+ contents: {
85
+ kind: MarkupKind.Markdown,
86
+ value: `**OIDC identity provider**\n\n\`${resource}\``,
87
+ },
88
+ };
89
+ }
90
+ if (resource.startsWith('saml-provider/')) {
91
+ return {
92
+ range: location.range,
93
+ contents: {
94
+ kind: MarkupKind.Markdown,
95
+ value: `**SAML identity provider**\n\n\`${resource}\``,
96
+ },
97
+ };
98
+ }
99
+ }
100
+ const knownProviders = {
101
+ 'cognito-identity.amazonaws.com': 'Amazon Cognito identity pool',
102
+ 'www.amazon.com': 'Login with Amazon',
103
+ 'accounts.google.com': 'Google',
104
+ 'graph.facebook.com': 'Facebook',
105
+ };
106
+ const description = knownProviders[location.value];
107
+ if (description) {
108
+ return {
109
+ range: location.range,
110
+ contents: {
111
+ kind: MarkupKind.Markdown,
112
+ value: `**Federated identity provider**\n\n${description}`,
113
+ },
114
+ };
115
+ }
116
+ }
117
+ return null;
118
+ }
@@ -0,0 +1,3 @@
1
+ import { type Hover } from 'vscode-languageserver';
2
+ import type { PrincipalValueLocation } from '../../lib/iam-policy/location.ts';
3
+ export declare function handlePrincipalValueHover(location: PrincipalValueLocation): Hover | null;
@@ -0,0 +1,13 @@
1
+ import { MarkupKind } from 'vscode-languageserver';
2
+ export function handlePrincipalValueHover(location) {
3
+ if (location.value === '*') {
4
+ return {
5
+ range: location.range,
6
+ contents: {
7
+ kind: MarkupKind.Markdown,
8
+ value: '**Public (unauthenticated) access**\n\nMatches all principals, including anonymous users.\n\n> **Warning:** Combining `"Principal": "*"` with `"Effect": "Allow"` grants public access. Always scope with a `Condition` element unless public access is intended.',
9
+ },
10
+ };
11
+ }
12
+ return null;
13
+ }
@@ -0,0 +1,3 @@
1
+ import { type Hover } from 'vscode-languageserver';
2
+ import type { ResourceValueLocation } from '../../lib/iam-policy/location.ts';
3
+ export declare function handleResourceValueHover(location: ResourceValueLocation): Hover | null;