AWS sns documentation change
Summary
Added Swift SDK code example demonstrating SNS/SQS integration scenario with FIFO topics, message filtering, and queue policies
Security assessment
The change demonstrates security best practices by showing proper queue policy configuration that restricts SQS access to specific SNS topics (aws:SourceArn condition). However, this is a standard security configuration example rather than addressing a specific security vulnerability.
Diff
diff --git a/sns/latest/dg/example_sqs_Scenario_TopicsAndQueues_section.md b/sns/latest/dg/example_sqs_Scenario_TopicsAndQueues_section.md index 91c14ff71..e3445a1dc 100644 --- a//sns/latest/dg/example_sqs_Scenario_TopicsAndQueues_section.md +++ b//sns/latest/dg/example_sqs_Scenario_TopicsAndQueues_section.md @@ -4066,0 +4067,734 @@ There's more on GitHub. Find the complete example and learn how to set up and ru +Swift + + +**SDK for Swift** + + +###### Note + +There's more on GitHub. Find the complete example and learn how to set up and run in the [AWS Code Examples Repository](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/swift/example_code/sqs/scenario#code-examples). + + + import ArgumentParser + import AWSClientRuntime + import AWSSNS + import AWSSQS + import Foundation + + struct ExampleCommand: ParsableCommand { + @Option(help: "Name of the Amazon Region to use") + var region = "us-east-1" + + static var configuration = CommandConfiguration( + commandName: "queue-scenario", + abstract: """ + This example interactively demonstrates how to use Amazon Simple + Notification Service (Amazon SNS) and Amazon Simple Queue Service + (Amazon SQS) together to publish and receive messages using queues. + """, + discussion: """ + Supports filtering using a "tone" attribute. + """ + ) + + /// Prompt for an input string. Only non-empty strings are allowed. + /// + /// - Parameter prompt: The prompt to display. + /// + /// - Returns: The string input by the user. + func stringRequest(prompt: String) -> String { + var str: String? + + while str == nil { + print(prompt, terminator: "") + str = readLine() + + if str != nil && str?.count == 0 { + str = nil + } + } + + return str! + } + + /// Ask a yes/no question. + /// + /// - Parameter prompt: A prompt string to print. + /// + /// - Returns: `true` if the user answered "Y", otherwise `false`. + func yesNoRequest(prompt: String) -> Bool { + while true { + let answer = stringRequest(prompt: prompt).lowercased() + if answer == "y" || answer == "n" { + return answer == "y" + } + } + } + + /// Display a menu of options then request a selection. + /// + /// - Parameters: + /// - prompt: A prompt string to display before the menu. + /// - options: An array of strings giving the menu options. + /// + /// - Returns: The index number of the selected option or 0 if no item was + /// selected. + func menuRequest(prompt: String, options: [String]) -> Int { + let numOptions = options.count + + if numOptions == 0 { + return 0 + } + + print(prompt) + + for (index, value) in options.enumerated() { + print("(\(index)) \(value)") + } + + repeat { + print("Enter your selection (0 - \(numOptions-1)): ", terminator: "") + if let answer = readLine() { + guard let answer = Int(answer) else { + print("Please enter the number matching your selection.") + continue + } + + if answer >= 0 && answer < numOptions { + return answer + } else { + print("Please enter the number matching your selection.") + } + } + } while true + } + + /// Ask the user too press RETURN. Accepts any input but ignores it. + /// + /// - Parameter prompt: The text prompt to display. + func returnRequest(prompt: String) { + print(prompt, terminator: "") + _ = readLine() + } + + var attrValues = [ + "<none>", + "cheerful", + "funny", + "serious", + "sincere" + ] + + /// Ask the user to choose one of the attribute values to use as a filter. + /// + /// - Parameters: + /// - message: A message to display before the menu of values. + /// - attrValues: An array of strings giving the values to choose from. + /// + /// - Returns: The string corresponding to the selected option. + func askForFilter(message: String, attrValues: [String]) -> String? { + print(message) + for (index, value) in attrValues.enumerated() { + print(" [\(index)] \(value)") + } + + var answer: Int? + repeat { + answer = Int(stringRequest(prompt: "Select an value for the 'tone' attribute or 0 to end: ")) + } while answer == nil || answer! < 0 || answer! > attrValues.count + 1 + + if answer == 0 { + return nil + } + return attrValues[answer!] + } + + /// Prompts the user for filter terms and constructs the attribute + /// record that specifies them. + /// + /// - Returns: A mapping of "FilterPolicy" to a JSON string representing + /// the user-defined filter. + func buildFilterAttributes() -> [String:String] { + var attr: [String:String] = [:] + var filterString = "" + + var first = true + + while let ans = askForFilter(message: "Choose a value to apply to the 'tone' attribute.", + attrValues: attrValues) { + if !first { + filterString += "," + } + first = false + + filterString += "\"\(ans)\"" + } + + let filterJSON = "{ \"tone\": [\(filterString)]}" + attr["FilterPolicy"] = filterJSON + + return attr + } + /// Create a queue, returning its URL string. + /// + /// - Parameters: + /// - prompt: A prompt to ask for the queue name. + /// - isFIFO: Whether or not to create a FIFO queue. + /// + /// - Returns: The URL of the queue. + func createQueue(prompt: String, sqsClient: SQSClient, isFIFO: Bool) async throws -> String? { + repeat { + var queueName = stringRequest(prompt: prompt) + var attributes: [String: String] = [:] + + if isFIFO { + queueName += ".fifo" + attributes["FifoQueue"] = "true" + } + + do { + let output = try await sqsClient.createQueue( + input: CreateQueueInput( + attributes: attributes, + queueName: queueName + ) + )