渗透测试预处理_快速进行单元测试预处理器宏

渗透测试预处理

Preprocessor macros are used to bring context to a build, allowing you to transform how your app is compiled depending on why it’s being built.

预处理器宏用于将上下文带入构建,从而使您可以根据构建原因来转换应用程序的编译方式。

In iOS, preprocessors are popularly used to determine which scheme was used to build an app. This allows you to build things such as debug viewswhich are only visible in debug builds, additional logging for non-AppStore builds, and more. My favorite use of macros is using the factory pattern to return mocked components for XCUITests:

在iOS中,通常使用预处理器来确定用于构建应用程序的方案。 这使您可以构建诸如仅在调试版本中可见的debug views内容,针对非AppStore版本的其他日志记录等。 我最喜欢的宏用法是使用工厂模式返回XCUITests的模拟组件:

Additionally, preprocessor macros have the incredible ability to change how your code is compiled. As the prefix “pre” implies, the value of a macro is tested before your code is compiled, meaning that everything in the “else” block of a preprocessor macros will not be present in the final binary.

此外,预处理器宏具有令人难以置信的功能,可以更改代码的编译方式。 就像前缀“ pre”所暗示的那样,宏的值会编译代码之前进行测试,这意味着预处理器宏的“ else”块中的所有内容都不会出现在最终的二进制文件中。

For things like debug viewsand mocked components, this is an amazing feature. As the code in the else block is ignored, a hacker that decompiles your app would not find a single trace that such features even existed.

对于debug views和模拟组件之类的东西,这是一个了不起的功能。 由于else块中的代码将被忽略,反编译您的应用程序的黑客不会找到甚至存在此类功能的单个跟踪。

预处理器宏单元测试问题 (Preprocessor Macros Unit Testing Issues)

However, this same amazing feature also brings amazing problems. Since the code is ignored, you might have witnessed the fact that testing them can be very difficult. Unless you create multiple testing schemes with multiple macro settings (please don’t do this), you are not easily able to test how your app behaves depending on the value of the macro — only the value that your testing scheme is currently using. For example, in the createClient() snippet above, I would able to test that createClient()returns a MockHTTPClient as my testing scheme defines the TESTING macro, but I wouldn’t be able to confirm that it returns a URLSessionHTTPClient in other situations.

但是,此相同的令人惊奇的功能也带​​来了令人惊奇的问题。 由于忽略了代码,因此您可能已经目睹了对它们进行测试可能非常困难的事实。 除非您使用多个宏设置创建多个测试方案(请不要这样做),否则您将不容易根据宏的值来测试应用程序的行为-仅测试方案当前正在使用的值。 例如,在上面的createClient()代码段中,当我的测试方案定义了TESTING宏时,我将能够测试createClient()返回的MockHTTPClient ,但是在其他情况下,我将无法确认其是否返回了URLSessionHTTPClient

This might lead you to other alternatives, like using compiler arguments or a global property instead of the macro directly:

这可能会导致您选择其他方法,例如使用编译器参数或全局属性而不是直接使用宏:

In this case, it’s a lot easier for a unit test to override the value of the macro, as all we have to do is toggle the global isDebugging property. Unfortunately, while this works, you’re giving up the macro’s ability to erase your code. The usage of if isDebugging in this case tells the compiler that the execution of userDidShakeDevice() is unknown in compile time due to the fact that the value of that boolean can change any time. This would compile the if block even if you’re running a non-debug build, allowing hackers to find the pushDebugMenu() method and use it for evil.

在这种情况下,单元测试覆盖宏的值要容易isDebugging ,因为我们要做的就是切换全局isDebugging属性。 不幸的是,尽管这样做有效,但是您放弃了宏擦除代码的功能。 在这种情况下, if isDebugging的用法告诉编译器,由于该布尔值可以随时更改,因此在编译时未知 userDidShakeDevice()的执行。 即使您正在运行非调试版本,这也将编译if块,从而使黑客能够找到pushDebugMenu()方法并将其用于邪恶。

利用优化设置对预处理器宏进行单元测试 (Leveraging Optimization Settings to Unit Test Preprocessor Macros)

Fortunately, by leveraging the Swift Compiler’s ability to ignore unused code, we can mix the best of both worlds and achieve macros that properly erase code and are easily testable at the same time.

幸运的是,通过利用Swift编译器忽略未使用代码的能力,我们可以混合使用两种方法的优点,并获得可以正确擦除代码并易于同时测试的宏。

To explain how to achieve this, let’s use a real example. In one of the companies I worked for, for bug-finding reasons, employees couldn’t install AppStore builds — only beta ones. This was done by detecting which build you had installed and checking your user’s permissions to see if you were allowed to use it. Otherwise, you would get an alert showing you where to download the correct version. The detection part was done like this:

为了说明如何实现此目的,我们使用一个真实的示例。 在我工作过的一家公司中,出于发现错误的原因,员工无法安装AppStore版本,只能安装Beta版本。 这是通过检测您安装了哪个版本并检查用户的权限以查看是否被允许使用来完成的。 否则,您将收到一条警告,显示向您下载正确版本的位置。 检测部分是这样完成的:

As we can see, this is exactly like our previous examples, with the exact same issues as we had no way to test that currentBuildType returned the correct build type for each build.

如我们所见,这与我们之前的示例完全一样,但存在完全相同的问题,因为我们无法测试currentBuildType为每个构建返回正确的构建类型。

Before learning how we can test this, let’s clean this up by abstracting the macro checks into separate types:

在学习如何进行测试之前,让我们通过将宏检查抽象为单独的类型来进行清理:

等待! 这与全局属性示例不存在相同的问题吗? (Wait! Doesn’t This Have the Same Problem As the Global Property Example?)

If this is what you thought, congratulations! The answer is it depends, and this is exactly what we’re going to leverage to unit test this class. The global property is problematic because its value can change in runtime, but our macro type’s active properties are immutable. If you compile your app without optimization (true for debug and testing builds), the compiler will behave exactly like in the global property example, but if you compile with optimization (true for release builds), since our active values are immutable, the compiler will erase the unreachable parts of the code.

如果这是您的想法,那么恭喜! 答案是取决于情况,而这正是我们将要用来对该类进行单元测试的内容。 全局属性是有问题的,因为它的值可以在运行时更改,但是宏类型的活动属性是不可变的。 如果您在没有优化的情况下编译您的应用程序(对于调试和测试版本,则为true),则编译器的行为将与全局属性示例中的行为完全相同;但是,如果您在进行优化的情况下(对于发行版,则为true),则编译器的行为将相同,因为我们的活动值是不变的将删除代码中无法访问的部分。

We can confirm this by dumping Swift’s SIL, which can be thought of as the final version of your code:

我们可以通过转储Swift的SIL来确认这一点,可以将其视为代码的最终版本:

The SIL indicates that when that file is compiled with optimization and the XCODE macro, currentBuildType instantly returns .xcodeBuild. Traces of the other build types in this method have been completely eliminated!

SIL表示在使用优化和XCODE宏编译该文件时, currentBuildType立即返回.xcodeBuild 。 此方法中其他构建类型的痕迹已被完全消除!

(Note: This method still isn’t hacker-proof because the other build types are still defined in the enum. Use this as an example of how to unit test macros, but consider the security necessities when implementing the trick shown in this article.)

(注意:此方法仍然不是防黑客的,因为其他构建类型仍在枚举中定义。以该方法为单位单元测试宏的示例,但在实现本文中所示的技巧时,请考虑安全性的必要性。 )

单元测试宏 (Unit Testing the Macros)

Finally, to test these macros, we can simply add a property to override its value when the macro isn’t present:

最后,要测试这些宏,我们可以简单地添加一个属性以在不存在该宏时覆盖其值:

Even though a hacker is able to force isOverriden to be true, nothing would be achieved as the code that ran as the result of the presence of this macro was never compiled.

即使黑客能够强迫isOverridentrue ,也不会实现任何目标,因为从未编译由于存在此宏而导致的代码。

Changing isOverriden would only have an effect if we compiled our code without optimizations, which is exactly the case of unit testing targets. We can then use it to safely unit test currentBuildType.

仅当我们在不优化的情况下编译代码时,更改isOverriden才会生效,这正是单元测试目标的情况。 然后,我们可以使用它来安全地对currentBuildType进行单元测试。

(Note that because isOverriden only works when a macro isn’t present in the build, your testing target should not contain macros whose’s presence/absence needs to be tested.)

(请注意,由于 isOverriden 仅在构建中不存在宏时才起作用,因此测试目标不应包含需要对其存在/不存在进行测试的宏。)

With this setup, we’re able to test preprocessor macros while being sure that our final release builds will not contain code that’s not meant to be executed. This same trick can be used to test macros in XCUITests.

通过此设置,我们可以测试预处理器宏,同时确保最终版本中不会包含不需要执行的代码。 可以使用相同的技巧来测试XCUITests中的宏。

As a final note in the topic of security, note that if your intention is to completely get rid of something in runtime, everything needs to be wrapped by macro checks. In this case, if hiding the build types themselves was critical (and not just the logic of what’s my current one), we would have to wrap the enum itself in macros, which would make unit testing it like this considerably harder, although still possible.

作为安全性主题的最后一点,请注意,如果您打算完全摆脱运行时中的某些内容,则所有内容都需要通过宏检查进行包装。 在这种情况下,如果隐藏构建类型本身很关键(而不仅仅是我当前的逻辑),我们将不得不将枚举自身包装在宏中,这将使单元测试变得相当困难,尽管仍然可能。

翻译自: https://medium.com/better-programming/unit-testing-preprocessor-macros-in-swift-b119b7a91afe

渗透测试预处理

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值